{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Title: #打家劫舍 II"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Difficulty: #Medium"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Category Title: #Algorithms"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tag Slug: #array #dynamic-programming"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Name Translated: #数组 #动态规划"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Solution Name: rob"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Translated Title: #打家劫舍 II"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Translated Content:\n",
    "<p>一个专业的小偷，计划偷窃一个环形街道上沿街的房屋，每间房内都藏有一定的现金。这个地方所有的房屋都 <strong>围成一圈</strong> ，这意味着第一个房屋和最后一个房屋是紧挨着的。同时，相邻的房屋装有相互连通的防盗系统，<strong>如果两间相邻的房屋在同一晚上被小偷闯入，系统会自动报警</strong> 。</p>\n",
    "\n",
    "<p>给定一个代表每个房屋存放金额的非负整数数组 <code>nums</code> ，请计算&nbsp;<strong>在不触动警报装置的情况下</strong> ，今晚能够偷窃到的最高金额。</p>\n",
    "\n",
    "<p>&nbsp;</p>\n",
    "\n",
    "<p><strong>示例&nbsp;1：</strong></p>\n",
    "\n",
    "<pre>\n",
    "<strong>输入：</strong>nums = [2,3,2]\n",
    "<strong>输出：</strong>3\n",
    "<strong>解释：</strong>你不能先偷窃 1 号房屋（金额 = 2），然后偷窃 3 号房屋（金额 = 2）, 因为他们是相邻的。\n",
    "</pre>\n",
    "\n",
    "<p><strong>示例 2：</strong></p>\n",
    "\n",
    "<pre>\n",
    "<strong>输入：</strong>nums = [1,2,3,1]\n",
    "<strong>输出：</strong>4\n",
    "<strong>解释：</strong>你可以先偷窃 1 号房屋（金额 = 1），然后偷窃 3 号房屋（金额 = 3）。\n",
    "&nbsp;    偷窃到的最高金额 = 1 + 3 = 4 。</pre>\n",
    "\n",
    "<p><strong>示例 3：</strong></p>\n",
    "\n",
    "<pre>\n",
    "<strong>输入：</strong>nums = [0]\n",
    "<strong>输出：</strong>0\n",
    "</pre>\n",
    "\n",
    "<p>&nbsp;</p>\n",
    "\n",
    "<p><strong>提示：</strong></p>\n",
    "\n",
    "<ul>\n",
    "\t<li><code>1 &lt;= nums.length &lt;= 100</code></li>\n",
    "\t<li><code>0 &lt;= nums[i] &lt;= 1000</code></li>\n",
    "</ul>\n",
    "\n",
    "<p>&nbsp;</p>\n",
    "\n",
    "<p><meta charset=\"UTF-8\" />注意：本题与主站 213&nbsp;题相同：&nbsp;<a href=\"https://leetcode-cn.com/problems/house-robber-ii/\">https://leetcode-cn.com/problems/house-robber-ii/</a></p>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Description: [PzWKhm](https://leetcode.cn/problems/PzWKhm/description/)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Solutions: [PzWKhm](https://leetcode.cn/problems/PzWKhm/solutions/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_cases = ['[2,3,2]', '[1,2,3,1]']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def countSubstrings(self, s: str, t: str) -> int:\n",
    "        ans, n, m = 0, len(s), len(t)\n",
    "        for d in range(1 - m, n):  # d=i-j, j=i-d\n",
    "            i = max(d, 0)\n",
    "            k0 = k1 = i - 1\n",
    "            while i < n and i - d < m:\n",
    "                if s[i] != t[i - d]:\n",
    "                    k0 = k1  # 上上一个不同\n",
    "                    k1 = i   # 上一个不同\n",
    "                ans += k1 - k0\n",
    "                i += 1\n",
    "        return ans\n",
    "\n",
    "# 作者：灵茶山艾府\n",
    "# 链接：https://leetcode.cn/problems/count-substrings-that-differ-by-one-character/solutions/2192600/tu-jie-fei-bao-li-onm-suan-fa-pythonjava-k5og/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    '''\n",
    "    做本题需要弄清楚两点：\n",
    "    1.最大距离就是求一棵树的最大直径（最大的子树边数，不是节点数）\n",
    "    2.如果把所有的合法子树都判断好\n",
    "    第一点比较容易，可以参考：https://leetcode.cn/problems/tree-diameter/\n",
    "    第二点根据题目给出的信息节点数1 - n，n的范围在2 - 15，所以可以用状态压缩，用整形mask表示子树中包含有哪些\n",
    "    节点，对应位置为1表示存在对应下标值的节点，0表示不存在对应下标值的节点\n",
    "    '''\n",
    "    def countSubgraphsForEachDiameter(self, n: int, edges: List[List[int]]) -> List[int]:\n",
    "        graph = defaultdict(list)\n",
    "        for u, v in edges:\n",
    "            graph[u - 1].append(v - 1)\n",
    "            graph[v - 1].append(u - 1)\n",
    "        \n",
    "        ans = [0] * (n - 1)\n",
    "        '''\n",
    "        外层枚举所有的节点组合，要有直径最少要有2个节点，对应mask从3开始\n",
    "        '''\n",
    "        for mask in range(3, 1 << n):\n",
    "            '''\n",
    "            如果有只有一个1，那么mask & mask - 1一定是0，这种方式能过滤掉只有一个1的情况\n",
    "            '''\n",
    "            if not (mask & (mask - 1)):\n",
    "                continue\n",
    "\n",
    "            vis, diameter = 0, 0\n",
    "            '''\n",
    "            以u节点为根节点，计算该子树的最长直径，此时可能mask对应的节点并不能组成一颗合法的树，\n",
    "            比如例1中只选择了1、3、4三个节点，对于这种情况，其实从任意一点往下走，都不可能走完整颗\n",
    "            树，所以用变量vis记录走过的所有节点，最终vis == mask说明当前选择的树是联通的\n",
    "            '''\n",
    "            def helper(u):\n",
    "                nonlocal vis, mask, diameter\n",
    "                vis |= 1 << u\n",
    "                mx, smx = 0, 0\n",
    "                for v in graph[u]:\n",
    "                    if vis & (1 << v) or not (mask & (1 << v)):\n",
    "                        continue\n",
    "                    res = helper(v) + 1\n",
    "                    if res > mx:\n",
    "                        smx = mx\n",
    "                        mx = res \n",
    "                    elif res > smx:\n",
    "                        smx = res\n",
    "                    \n",
    "                    diameter = max(diameter, mx + smx)\n",
    "                return mx\n",
    "            '''\n",
    "            bit_length()表示二进制第一个1是第几位数，从低位到高位算（右到左），减一就等于第一位从0开始的下标，\n",
    "            正好就是选择了一个节点\n",
    "            '''\n",
    "            helper(mask.bit_length() - 1)\n",
    "            if vis == mask:\n",
    "                ans[diameter - 1] += 1\n",
    "        \n",
    "        return ans"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "# https://leetcode.cn/problems/cut-off-trees-for-golf-event/solutions/1512295/by-fuxuemingzhu-dtet/\n",
    "class Solution:\n",
    "    def cutOffTree(self, forest: List[List[int]]) -> int:\n",
    "        def bfs(start_x, start_y,final_x,final_y):\n",
    "\n",
    "            def insideBoard(x,y):\n",
    "                return 0 <= x < rows and 0 <= y < cols\n",
    "\n",
    "            if start_x == final_x and start_y == final_y:\n",
    "                return 0\n",
    "\n",
    "            seen = set()\n",
    "            seen.add((start_x,start_y))\n",
    "            queue = [(start_x,start_y)]\n",
    "            directions = [(1,0),(-1,0),(0,1),(0,-1)]\n",
    "            steps = 0\n",
    "            while queue:\n",
    "                n = len(queue)\n",
    "                for _ in range(n):\n",
    "                    x,y = queue.pop(0)\n",
    "                    if x == final_x and y == final_y:\n",
    "                        return steps\n",
    "                    for d in directions:\n",
    "                        nx, ny = x+d[0],y+d[1]\n",
    "                        if 0 <= nx < rows and 0 <= ny < cols and forest[nx][ny] != 0 and (nx,ny) not in seen:\n",
    "                            seen.add((nx,ny))\n",
    "                            queue.append((nx,ny))\n",
    "                steps += 1\n",
    "            return -1\n",
    "                            \n",
    "\n",
    "\n",
    "\n",
    "        rows = len(forest)\n",
    "        cols = len(forest[0])\n",
    "        trees = []\n",
    "        for i in range(rows):\n",
    "            for j in range(cols):\n",
    "                if forest[i][j] > 1:\n",
    "                    trees.append((forest[i][j],i,j))\n",
    "        trees.sort()\n",
    "\n",
    "        start_x, start_y = 0, 0\n",
    "        ans = 0\n",
    "        for height, x, y in trees:\n",
    "            distance = bfs(start_x, start_y,x,y)\n",
    "            if distance == -1:\n",
    "                return -1\n",
    "            ans += distance\n",
    "            start_x,start_y = x,y\n",
    "        return ans\n",
    "            "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "import random\n",
    "class Solution:\n",
    "    def __init__(self, w: List[int]):\n",
    "        sw = sum(w)\n",
    "        self.probs = []\n",
    "        accu = 0.0\n",
    "        for v in w:\n",
    "            accu += v\n",
    "            self.probs.append(accu / sw)\n",
    "\n",
    "    def find(self, target):\n",
    "        l, r = 0, len(self.probs)-1\n",
    "        while l < r:\n",
    "            m = l + (r-l) // 2\n",
    "            if self.probs[m] < target:\n",
    "                l = m + 1\n",
    "            else:\n",
    "                r = m\n",
    "        return l\n",
    "\n",
    "\n",
    "    def pickIndex(self) -> int:\n",
    "        r = random.random()\n",
    "        t = self.find(r)\n",
    "        return t\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "# Your Solution object will be instantiated and called as such:\n",
    "# obj = Solution(w)\n",
    "# param_1 = obj.pickIndex()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "\n",
    "    def __init__(self, w: List[int]):\n",
    "        self.w = w\n",
    "        sum_ = sum(self.w)\n",
    "        self.prob = []\n",
    "        for i in range(len(self.w)):\n",
    "            self.prob.append(self.w[i]/sum_)\n",
    "        self.cumsum = []\n",
    "        self.cumsum.append(0)\n",
    "        for i in range(len(self.prob)):\n",
    "            self.cumsum.append(self.cumsum[-1]+self.prob[i])\n",
    "\n",
    "\n",
    "    def pickIndex(self) -> int:\n",
    "        #print(self.cumsum)\n",
    "        x = random.random()\n",
    "        l = bisect_left(self.cumsum,x)\n",
    "        #print(l)\n",
    "        return l-1\n",
    "\n",
    "\n",
    "# Your Solution object will be instantiated and called as such:\n",
    "# obj = Solution(w)\n",
    "# param_1 = obj.pickIndex()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "from typing import List\n",
    "\n",
    "\n",
    "class PerfectBinaryTree:\n",
    "    \"\"\"\n",
    "    完全二叉树.\n",
    "    根节点编号为1,左子节点编号为2*val,右子节点编号为2*val+1.\n",
    "    \"\"\"\n",
    "\n",
    "    @staticmethod\n",
    "    def depth(u: int) -> int:\n",
    "        if u == 0:\n",
    "            return 0\n",
    "        return u.bit_length() - 1\n",
    "\n",
    "    @staticmethod\n",
    "    def lca(u: int, v: int) -> int:\n",
    "        if u == v:\n",
    "            return u\n",
    "        if u > v:\n",
    "            u, v = v, u\n",
    "        depth1, depth2 = PerfectBinaryTree.depth(u), PerfectBinaryTree.depth(v)\n",
    "        diff = u ^ (v >> (depth2 - depth1))\n",
    "        if diff == 0:\n",
    "            return u\n",
    "        len_ = diff.bit_length()\n",
    "        return u >> len_\n",
    "\n",
    "    @staticmethod\n",
    "    def dist(u: int, v: int) -> int:\n",
    "        return (\n",
    "            PerfectBinaryTree.depth(u)\n",
    "            + PerfectBinaryTree.depth(v)\n",
    "            - 2 * PerfectBinaryTree.depth(PerfectBinaryTree.lca(u, v))\n",
    "        )\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    # https://leetcode.cn/problems/cycle-length-queries-in-a-tree/description/\n",
    "    class Solution:\n",
    "        def cycleLengthQueries(self, n: int, queries: List[List[int]]) -> List[int]:\n",
    "            res = [0] * len(queries)\n",
    "            for i, (root1, root2) in enumerate(queries):\n",
    "                res[i] = PerfectBinaryTree.dist(root1, root2) + 1\n",
    "            return res\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def cycleLengthQueries(self, n: int, queries: List[List[int]]) -> List[int]:\n",
    "        def getDepth(root: int) -> int:\n",
    "            return root.bit_length() - 1\n",
    "\n",
    "        def getLCA(a: int, b: int) -> int:\n",
    "            \"\"\"\n",
    "            https://leetcode.cn/problems/cycle-length-queries-in-a-tree/solutions/1/shinei-by-vclip-ch6n/\n",
    "            1. 一个结点从最高位的 1开始,往下遍历每一个二进制位,得到的就是从根节点出发走到这个结点的路径选择,\n",
    "               0代表向左,1代表向右;\n",
    "            2. 求出两个结点的深度，然后把较深结点向上移动，对齐到另一个节点，让两者的二进制位对应上;\n",
    "            3. 之后，对两个二进制数求异或，异或值就代表了两者选择路径时走的方向不同的位置，\n",
    "               异或为0,说明a就是LCA;否则上跳\n",
    "            \"\"\"\n",
    "            if a == b:\n",
    "                return a\n",
    "            if a > b:\n",
    "                a, b = b, a\n",
    "            depthA, depthB = a.bit_length() - 1, b.bit_length() - 1\n",
    "            diff = a ^ (b >> (depthB - depthA))\n",
    "            if diff == 0:\n",
    "                return a\n",
    "            len_ = diff.bit_length()\n",
    "            return a >> len_\n",
    "\n",
    "        res = [0] * len(queries)\n",
    "        for i, (root1, root2) in enumerate(queries):\n",
    "            lca = getLCA(root1, root2)\n",
    "            res[i] = getDepth(root1) + getDepth(root2) - 2 * getDepth(lca) + 1\n",
    "        return res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class HitCounter:\n",
    "\n",
    "    def __init__(self):\n",
    "        self.hits = collections.deque()\n",
    "\n",
    "    def hit(self, timestamp: int) -> None:\n",
    "        self.hits.append(timestamp)\n",
    "\n",
    "    def getHits(self, timestamp: int) -> int:\n",
    "        while self.hits and self.hits[0] <= timestamp - 300:\n",
    "            self.hits.popleft()\n",
    "        return len(self.hits)\n",
    "\n",
    "# 作者：roylx\n",
    "# 链接：https://leetcode.cn/problems/design-hit-counter/solutions/2332683/python-362-dui-lie-er-fen-qian-zhui-he-b-0cfb/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Allocator:\n",
    "    def __init__(self, n: int):\n",
    "        self.a = [0] * n\n",
    "\n",
    "    def allocate(self, size: int, mID: int) -> int:\n",
    "        cnt = 0\n",
    "        for i, id in enumerate(self.a):\n",
    "            if id:\n",
    "                cnt = 0\n",
    "            else:\n",
    "                cnt += 1\n",
    "                if cnt == size:\n",
    "                    self.a[i - size + 1: i + 1] = [mID] * size\n",
    "                    return i - size + 1\n",
    "        return -1\n",
    "\n",
    "    def free(self, mID: int) -> int:\n",
    "        cnt = 0\n",
    "        for i, id in enumerate(self.a):\n",
    "            if id == mID:\n",
    "                cnt += 1\n",
    "                self.a[i] = 0\n",
    "        return cnt\n",
    "\n",
    "# 作者：灵茶山艾府\n",
    "# 链接：https://leetcode.cn/problems/design-memory-allocator/solutions/2016010/bao-li-mo-ni-by-endlesscheng-bqba/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "import random\n",
    "MAX_LEVEL = 32\n",
    "PROB = 0.25\n",
    "# class LinkNode:\n",
    "#     def __init__(self, val = 0):\n",
    "#         self.val = val\n",
    "#         self.nxt = None\n",
    "#         self.nxtlevel = None\n",
    "\n",
    "class SkipListNode:\n",
    "    __slots__  = 'val', 'nxt'\n",
    "\n",
    "    def __init__(self, val, max_level = MAX_LEVEL):\n",
    "        self.val = val\n",
    "        self.nxt = [None] * max_level\n",
    "\n",
    "def randomLevel():\n",
    "    lv = 1\n",
    "    while lv < MAX_LEVEL and random.random() < PROB:\n",
    "        lv += 1\n",
    "    return lv\n",
    "\n",
    "class Skiplist:\n",
    "\n",
    "    def __init__(self):\n",
    "        self.head = SkipListNode(-1)\n",
    "        self.level = 0\n",
    "        # self.level = 1\n",
    "        # self.head = LinkNode()\n",
    "        # self.prob = 0.5\n",
    "\n",
    "    def search(self, target: int) -> bool:\n",
    "        cur = self.head\n",
    "        for i in range(self.level - 1, -1, -1):\n",
    "            while cur.nxt[i] and cur.nxt[i].val < target:\n",
    "                cur = cur.nxt[i]\n",
    "        cur = cur.nxt[0]\n",
    "        return cur is not None and cur.val == target\n",
    "\n",
    "\n",
    "    def add(self, num: int) -> None:\n",
    "        load = [self.head] * MAX_LEVEL\n",
    "        cur = self.head\n",
    "        for i in range(self.level - 1, -1, -1):\n",
    "            while cur.nxt[i] and cur.nxt[i].val < num:\n",
    "                cur = cur.nxt[i]\n",
    "            load[i] = cur\n",
    "        newLevel = randomLevel()\n",
    "        self.level = max(self.level, newLevel)\n",
    "        newNode = SkipListNode(num, newLevel)\n",
    "        for i in range(newLevel):\n",
    "            newNode.nxt[i] = load[i].nxt[i]\n",
    "            load[i].nxt[i] = newNode\n",
    "\n",
    "    def erase(self, num: int) -> bool:\n",
    "        load = [None] * MAX_LEVEL\n",
    "        cur = self.head\n",
    "        for i in range(self.level - 1, -1, -1):\n",
    "            while cur.nxt[i] and cur.nxt[i].val < num:\n",
    "                cur = cur.nxt[i]\n",
    "            load[i] = cur\n",
    "        cur = cur.nxt[0]\n",
    "        if not cur or cur.val != num:\n",
    "            return False\n",
    "        for i in range(self.level):\n",
    "            if load[i].nxt[i] != cur:\n",
    "                break\n",
    "            load[i].nxt[i] =  cur.nxt[i]\n",
    "        while self.level > 1 and not self.head.nxt[self.level - 1]:\n",
    "            self.level -= 1\n",
    "        return True\n",
    "\n",
    "    # def isNxtLevelNeeded(self):\n",
    "    #     return random.random() < self.prob\n",
    "\n",
    "    # def search(self, target: int) -> bool:\n",
    "    #     node = self.head\n",
    "    #     while node:\n",
    "    #         while node.nxt and node.nxt.val < target:\n",
    "    #             node = node.nxt\n",
    "    #         if node.nxt and node.nxt.val == target:\n",
    "    #             return True\n",
    "    #         node = node.nxtlevel\n",
    "    #     return False\n",
    "\n",
    "    # def add(self, num: int) -> None:\n",
    "    #     node = LinkNode(num)\n",
    "    #     cur = self.head\n",
    "    #     stack = []\n",
    "    #     while cur:\n",
    "    #         while cur.nxt and cur.nxt.val < num:\n",
    "    #             cur = cur.nxt\n",
    "    #         if cur.nxt and cur.nxt.val == num:\n",
    "    #             return\n",
    "    #         stack.append(cur)\n",
    "    #         cur = cur.nxtlevel\n",
    "    #     nxtLevelNode = None\n",
    "    #     while stack:\n",
    "    #         prev = stack.pop()\n",
    "    #         tmp = LinkNode(num)\n",
    "    #         tmp.nxt = prev.nxt\n",
    "    #         prev.nxt = tmp\n",
    "    #         tmp.nxtlevel = nxtLevelNode\n",
    "    #         nxtLevelNode = tmp\n",
    "    #     if nxtLevelNode:\n",
    "    #         newHead = LinkNode()\n",
    "    #         newHead.nxt = nxtLevelNode\n",
    "    #         newHead.nxtlevel = self.head\n",
    "    #         self.head = newHead\n",
    "    #         self.level += 1\n",
    "\n",
    "    # def erase(self, num: int) -> bool:\n",
    "    #     node = self.head\n",
    "    #     while node:\n",
    "    #         while node.nxt and node.nxt.val < num:\n",
    "    #             node = node.nxt\n",
    "    #         if node.nxt and node.nxt.val == num:\n",
    "    #             node.nxt = node.nxt.nxt\n",
    "    #             return True\n",
    "    #         node = node.nxtlevel\n",
    "    #     return False\n",
    "    \n",
    "    # def insert(self, cur, pre, tar):\n",
    "\n",
    "\n",
    "# Your Skiplist object will be instantiated and called as such:\n",
    "# obj = Skiplist()\n",
    "# param_1 = obj.search(target)\n",
    "# obj.add(num)\n",
    "# param_3 = obj.erase(num)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Tweet:\n",
    "    def __init__(self, tId, time):\n",
    "        self.id = tId\n",
    "        self.time = time\n",
    "        self.next = None\n",
    "\n",
    "class User:\n",
    "    def __init__(self, uId):\n",
    "        self.id = uId\n",
    "        self.followed = set()\n",
    "        self.head = None\n",
    "\n",
    "        self.follow(uId)\n",
    "    \n",
    "    def follow(self, uId):\n",
    "        self.followed.add(uId)\n",
    "    \n",
    "    def unfollow(self, uId):\n",
    "        if self.id != uId and uId in self.followed:\n",
    "            self.followed.remove(uId)\n",
    "        \n",
    "    def post(self, tId, time):\n",
    "        tweet = Tweet(tId, time)\n",
    "        tweet.next = self.head\n",
    "        self.head = tweet\n",
    "\n",
    "class Twitter:\n",
    "\n",
    "    def __init__(self):\n",
    "        self.timeStamp = 0\n",
    "        self.userMap = {}\n",
    "\n",
    "    def postTweet(self, userId: int, tweetId: int) -> None:\n",
    "        if userId not in self.userMap:\n",
    "            self.userMap[userId] = User(userId)\n",
    "        self.userMap[userId].post(tweetId, self.timeStamp)\n",
    "        self.timeStamp += 1\n",
    "\n",
    "    def getNewsFeed(self, userId: int) -> List[int]:\n",
    "        heap = []\n",
    "        heapq.heapify(heap)\n",
    "\n",
    "        if userId in self.userMap:\n",
    "            for uid in self.userMap[userId].followed:\n",
    "                tweet = self.userMap[uid].head\n",
    "                while tweet:\n",
    "                    heap.append(tweet)\n",
    "                    tweet = tweet.next\n",
    "            \n",
    "            return [twt.id for twt in heapq.nlargest(10, heap, key=lambda twt: twt.time)]\n",
    "        \n",
    "        else:\n",
    "            return []\n",
    "\n",
    "    def follow(self, followerId: int, followeeId: int) -> None:\n",
    "        if followerId not in self.userMap:\n",
    "            self.userMap[followerId] = User(followerId)\n",
    "        if followeeId not in self.userMap:\n",
    "            self.userMap[followeeId] = User(followeeId)\n",
    "        \n",
    "        self.userMap[followerId].follow(followeeId)\n",
    "\n",
    "    def unfollow(self, followerId: int, followeeId: int) -> None:\n",
    "        if self.userMap[followerId]:\n",
    "            self.userMap[followerId].unfollow(followeeId)\n",
    "\n",
    "\n",
    "\"\"\"\n",
    "class Tweet:\n",
    "    def __init__(self, tId, time):\n",
    "        self.id = tId\n",
    "        self.time = time\n",
    "        self.next = None\n",
    "    \n",
    "class User:\n",
    "    def __init__(self, uId):\n",
    "        self.id = uId\n",
    "        self.followed = set()\n",
    "        self.cur = None\n",
    "\n",
    "        self.follow(uId)\n",
    "    \n",
    "    def follow(self, uId):\n",
    "        self.followed.add(uId)\n",
    "    \n",
    "    def unfollow(self, uId):\n",
    "        if uId != self.id and uId in self.followed:\n",
    "            self.followed.remove(uId)\n",
    "    \n",
    "    def post(self, tId, time):\n",
    "        tweet = Tweet(tId, time)\n",
    "        tweet.next = self.cur\n",
    "        self.cur = tweet\n",
    "\n",
    "class Twitter:\n",
    "\n",
    "    def __init__(self):\n",
    "        self.timeStamp = 0\n",
    "        self.userMap = {}\n",
    "\n",
    "    def postTweet(self, userId: int, tweetId: int) -> None:\n",
    "        if userId not in self.userMap:\n",
    "            self.userMap[userId] = User(userId)\n",
    "        \n",
    "        u = self.userMap[userId]\n",
    "        u.post(tweetId, self.timeStamp)\n",
    "        self.timeStamp += 1\n",
    "\n",
    "    def getNewsFeed(self, userId: int) -> List[int]:\n",
    "        heap = []\n",
    "        heapq.heapify(heap)\n",
    "\n",
    "        if userId in self.userMap:\n",
    "            user = self.userMap[userId]\n",
    "            for uid in user.followed:\n",
    "                tweets = self.userMap[uid].cur\n",
    "                while tweets:\n",
    "                    heap.append(tweets)\n",
    "                    tweets = tweets.next\n",
    "        \n",
    "            return [twt.id for twt in heapq.nlargest(10, heap, key=lambda twt: twt.time)]\n",
    "        else:\n",
    "            return []\n",
    "\n",
    "\n",
    "    def follow(self, followerId: int, followeeId: int) -> None:\n",
    "        if followerId not in self.userMap:\n",
    "            self.userMap[followerId] = User(followerId)\n",
    "        if followeeId not in self.userMap:\n",
    "            self.userMap[followeeId] = User(followeeId)\n",
    "        \n",
    "        self.userMap[followerId].follow(followeeId)\n",
    "\n",
    "    def unfollow(self, followerId: int, followeeId: int) -> None:\n",
    "        if followerId in self.userMap:\n",
    "            self.userMap[followerId].unfollow(followeeId)\n",
    "\n",
    "# https://leetcode.cn/problems/design-twitter/solutions/13675/mian-xiang-dui-xiang-she-ji-he-bing-k-ge-you-xu-li/\n",
    "\"\"\"\n",
    "# Your Twitter object will be instantiated and called as such:\n",
    "# obj = Twitter()\n",
    "# obj.postTweet(userId,tweetId)\n",
    "# param_2 = obj.getNewsFeed(userId)\n",
    "# obj.follow(followerId,followeeId)\n",
    "# obj.unfollow(followerId,followeeId)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Tweet:\n",
    "    def __init__(self, tId, time):\n",
    "        self.id = tId\n",
    "        self.time = time\n",
    "        self.next = None\n",
    "\n",
    "class User:\n",
    "    def __init__(self, uId):\n",
    "        self.id = uId\n",
    "        self.followed = set()\n",
    "        self.head = None\n",
    "        self.follow(uId)\n",
    "    \n",
    "    def follow(self, uId):\n",
    "        self.followed.add(uId)\n",
    "    \n",
    "    def unfollow(self, uId):\n",
    "        if self.id != uId and uId in self.followed:\n",
    "            self.followed.remove(uId)\n",
    "\n",
    "    def post(self, tId, time):\n",
    "        tweet = Tweet(tId, time)\n",
    "        tweet.next = self.head\n",
    "        self.head = tweet\n",
    "\n",
    "class Twitter:\n",
    "\n",
    "    def __init__(self):\n",
    "        self.timeStamp = 0\n",
    "        self.userMap = {}\n",
    "\n",
    "    def postTweet(self, userId: int, tweetId: int) -> None:\n",
    "        if userId not in self.userMap:\n",
    "            self.userMap[userId] = User(userId)\n",
    "        \n",
    "        self.userMap[userId].post(tweetId, self.timeStamp)\n",
    "        self.timeStamp += 1\n",
    "\n",
    "    def getNewsFeed(self, userId: int) -> List[int]:\n",
    "        heap = []\n",
    "        heapq.heapify(heap)\n",
    "\n",
    "        if userId in self.userMap:\n",
    "            user = self.userMap[userId]\n",
    "            for uid in user.followed:\n",
    "                tweets = self.userMap[uid].head\n",
    "                while tweets:\n",
    "                    heap.append(tweets)\n",
    "                    tweets = tweets.next\n",
    "            return [twt.id for twt in heapq.nlargest(10, heap, key=lambda twt: twt.time)]\n",
    "        else:\n",
    "            return []\n",
    "\n",
    "    def follow(self, followerId: int, followeeId: int) -> None:\n",
    "        if followerId not in self.userMap:\n",
    "            self.userMap[followerId] = User(followerId)\n",
    "        if followeeId not in self.userMap:\n",
    "            self.userMap[followeeId] = User(followeeId)\n",
    "        \n",
    "        self.userMap[followerId].follow(followeeId)\n",
    "\n",
    "    def unfollow(self, followerId: int, followeeId: int) -> None:\n",
    "        if followerId in self.userMap:\n",
    "            self.userMap[followerId].unfollow(followeeId)\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\"\"\"\n",
    "class Tweet:\n",
    "    def __init__(self, tId, time):\n",
    "        self.id = tId\n",
    "        self.time = time\n",
    "        self.next = None\n",
    "    \n",
    "class User:\n",
    "    def __init__(self, uId):\n",
    "        self.id = uId\n",
    "        self.followed = set()\n",
    "        self.cur = None\n",
    "\n",
    "        self.follow(uId)\n",
    "    \n",
    "    def follow(self, uId):\n",
    "        self.followed.add(uId)\n",
    "    \n",
    "    def unfollow(self, uId):\n",
    "        if uId != self.id and uId in self.followed:\n",
    "            self.followed.remove(uId)\n",
    "    \n",
    "    def post(self, tId, time):\n",
    "        tweet = Tweet(tId, time)\n",
    "        tweet.next = self.cur\n",
    "        self.cur = tweet\n",
    "\n",
    "class Twitter:\n",
    "\n",
    "    def __init__(self):\n",
    "        self.timeStamp = 0\n",
    "        self.userMap = {}\n",
    "\n",
    "    def postTweet(self, userId: int, tweetId: int) -> None:\n",
    "        if userId not in self.userMap:\n",
    "            self.userMap[userId] = User(userId)\n",
    "        \n",
    "        u = self.userMap[userId]\n",
    "        u.post(tweetId, self.timeStamp)\n",
    "        self.timeStamp += 1\n",
    "\n",
    "    def getNewsFeed(self, userId: int) -> List[int]:\n",
    "        heap = []\n",
    "        heapq.heapify(heap)\n",
    "\n",
    "        if userId in self.userMap:\n",
    "            user = self.userMap[userId]\n",
    "            for uid in user.followed:\n",
    "                tweets = self.userMap[uid].cur\n",
    "                while tweets:\n",
    "                    heap.append(tweets)\n",
    "                    tweets = tweets.next\n",
    "        \n",
    "            return [twt.id for twt in heapq.nlargest(10, heap, key=lambda twt: twt.time)]\n",
    "        else:\n",
    "            return []\n",
    "\n",
    "\n",
    "    def follow(self, followerId: int, followeeId: int) -> None:\n",
    "        if followerId not in self.userMap:\n",
    "            self.userMap[followerId] = User(followerId)\n",
    "        if followeeId not in self.userMap:\n",
    "            self.userMap[followeeId] = User(followeeId)\n",
    "        \n",
    "        self.userMap[followerId].follow(followeeId)\n",
    "\n",
    "    def unfollow(self, followerId: int, followeeId: int) -> None:\n",
    "        if followerId in self.userMap:\n",
    "            self.userMap[followerId].unfollow(followeeId)\n",
    "\n",
    "# https://leetcode.cn/problems/design-twitter/solutions/13675/mian-xiang-dui-xiang-she-ji-he-bing-k-ge-you-xu-li/\n",
    "\"\"\"\n",
    "\n",
    "# Your Twitter object will be instantiated and called as such:\n",
    "# obj = Twitter()\n",
    "# obj.postTweet(userId,tweetId)\n",
    "# param_2 = obj.getNewsFeed(userId)\n",
    "# obj.follow(followerId,followeeId)\n",
    "# obj.unfollow(followerId,followeeId)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def isReachableAtTime(self, sx: int, sy: int, fx: int, fy: int, t: int) -> bool:\n",
    "        \"\"\"\n",
    "        https://leetcode.com/problems/determine-if-a-cell-is-reachable-at-a-given-time/solutions/4024786/java-c-python-o-1/\n",
    "        检查我们需要的最小步长 = max（abs（sx - fx），abs（sy - fy））。\n",
    "        如果 need > 0，则检查 t 是否 >= need。\n",
    "        如果我们能更早到达，就可以在其相邻单元格附近停留。\n",
    "        如果 need == 0，目标单元格就是起始单元格。\n",
    "        如果 t == 1，我们将离开当前单元格，无法到达。\n",
    "        否则，我们可以回到起始单元格。\n",
    "        \"\"\"\n",
    "        need = max(abs(sx - fx), abs(sy - fy))\n",
    "        return t >= need if need else t != 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    \"\"\"\n",
    "    旋转坐标的关系：https://leetcode.cn/problems/determine-whether-matrix-can-be-obtained-by-rotation/description/comments/1962616\n",
    "    转度数          原坐标         转换坐标\n",
    "        90°            [x, y]   ->    [y, n - x - 1]\n",
    "        180°           [x, y]   ->    [n - x - 1, n - y - 1]\n",
    "        270°           [x, y]   ->    [n - y - 1, x]\n",
    "    \"\"\"\n",
    "    def findRotation(self, mat: List[List[int]], target: List[List[int]]) -> bool:\n",
    "        def equal(mat, target, rows, cols):\n",
    "            for i in range(rows):\n",
    "                for j in range(cols):\n",
    "                    if mat[i][j] != target[i][j]:\n",
    "                        return False\n",
    "            return True\n",
    "        rows, cols = mat.__len__(), mat[0].__len__()\n",
    "        k = rows // cols\n",
    "        import copy\n",
    "        for roundtime in range(1, 5):\n",
    "            if equal(mat, target, rows, cols):\n",
    "                return True\n",
    "            mat_ori = copy.deepcopy(mat)\n",
    "            for i in range(rows):\n",
    "                for j in range(cols):\n",
    "                    mat[j][rows-i-1] = mat_ori[i][j]\n",
    "            mat_ori = copy.deepcopy(mat)\n",
    "            \n",
    "        return False\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def dieSimulator(self, n: int, rollMax: List[int]) -> int:\n",
    "        # https://leetcode.cn/problems/dice-roll-simulation/solutions/492200/ni-bi-dong-chao-jian-dan-dong-tai-gui-hua-fu-za-du/\n",
    "        MOD = 10 ** 9 + 7\n",
    "        dp = [[0] * 6 for _ in range(n)]\n",
    "        dp[0] = [1] * 6\n",
    "        for i in range(1, n):\n",
    "            dp[i] = [sum(dp[i-1])] * 6\n",
    "            for j, back in enumerate(rollMax):\n",
    "                if i - back > 0:\n",
    "                    dp[i][j] -= sum(dp[i-back-1]) - dp[i-back-1][j]\n",
    "                    # 为什么不要这句？ \n",
    "                    # dp[i][j] -= dp[i-back][j]\n",
    "                elif i - back == 0:\n",
    "                    dp[i][j] -= 1\n",
    "                dp[i][j] %= MOD\n",
    "\n",
    "        # print(dp)\n",
    "        # return s[i] % MOD\n",
    "        return sum(dp[-1]) % MOD\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def maxOutput(self, n: int, edges: List[List[int]], price: List[int]) -> int:\n",
    "        g = [[] for _ in range(n)]\n",
    "        for x, y in edges:\n",
    "            g[x].append(y)\n",
    "            g[y].append(x)  # 建树\n",
    "\n",
    "        ans = 0\n",
    "        def dfs(x: int, fa: int) -> (int, int):\n",
    "            nonlocal ans\n",
    "            max_s1 = p = price[x]\n",
    "            max_s2 = 0\n",
    "            for y in g[x]:\n",
    "                if y == fa: continue\n",
    "                s1, s2 = dfs(y, x)\n",
    "                # 前面最大带叶子的路径和 + 当前不带叶子的路径和\n",
    "                # 前面最大不带叶子的路径和 + 当前带叶子的路径和\n",
    "                ans = max(ans, max_s1 + s2, max_s2 + s1)\n",
    "                max_s1 = max(max_s1, s1 + p)\n",
    "                max_s2 = max(max_s2, s2 + p)  # 这里加上 p 是因为 x 必然不是叶子\n",
    "            return max_s1, max_s2\n",
    "        dfs(0, -1)\n",
    "        return ans\n",
    "\n",
    "# 作者：灵茶山艾府\n",
    "# 链接：https://leetcode.cn/problems/difference-between-maximum-and-minimum-price-sum/solutions/2062782/by-endlesscheng-5l70/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "# ### 12/58\n",
    "# from collections import defaultdict\n",
    "# class Solution:\n",
    "#     def maxOutput(self, n: int, edges: List[List[int]], price: List[int]) -> int:\n",
    "#         g=defaultdict(list)\n",
    "#         for u,v in edges:\n",
    "#             g[u].append(v)\n",
    "#             g[v].append(u)\n",
    "\n",
    "#         self.ans=0\n",
    "#         def dfs(x,fa):\n",
    "#             mi=price[x]\n",
    "#             mx=price[x]\n",
    "#             for y in g[x]:\n",
    "#                 if y!=fa:\n",
    "#                     nxt=dfs(y,x)\n",
    "#                     cur=price[x]+nxt \n",
    "#                     self.ans=max(self.ans,cur-mi)\n",
    "#                     mx=max(mx,cur)\n",
    "#             return mx\n",
    "#         dfs(0,-1)\n",
    "#         return self.ans\n",
    "\n",
    "# 4\n",
    "# [[2,0],[0,1],[1,3]]\n",
    "# [2,3,1,1]  --- ans:6  测例不通过\n",
    "\n",
    "# ### pass rate 52/58\n",
    "### 这种思路不能全部通过的原因：可能存在多条线路是最大树直径值，但头节点的大小不一；我只计算了一条直径线路的去头节点值（不一定是全树的最大差值）\n",
    "# from collections import defaultdict\n",
    "# class Solution:\n",
    "#     def maxOutput(self, n: int, edges: List[List[int]], price: List[int]) -> int:\n",
    "#         g=defaultdict(list)\n",
    "#         for u,v in edges:\n",
    "#             g[u].append(v)\n",
    "#             g[v].append(u)\n",
    "\n",
    "#         def dfs(x,fa,v):\n",
    "#             nonlocal mx,idx\n",
    "#             v+=price[x] ###确认每一轮都已经添加当前节点value（在比较mx之前）\n",
    "#             if v>mx:\n",
    "#                 mx=v\n",
    "#                 idx=x\n",
    "#             for y in g[x]:\n",
    "#                 if y!=fa:\n",
    "#                     dfs(y,x,v)\n",
    "#             return mx,idx\n",
    "\n",
    "#         mx,idx=0,-1\n",
    "#         _,node1=dfs(0,-1,0)\n",
    "#         mx,idx=0,-1\n",
    "#         v,node2=dfs(node1,-1,0)\n",
    "#         return v-min(price[node1],price[node2])\n",
    "\n",
    "\n",
    "# ### S-code\n",
    "# ###　Ｓ－ｃｏｄｅ\n",
    "# class Solution:\n",
    "#     def maxOutput(self, n: int, edges: List[List[int]], price: List[int]) -> int:\n",
    "\n",
    "#         dct = [[] for _ in range(n)]\n",
    "#         for i, j in edges:\n",
    "#             dct[i].append(j)\n",
    "#             dct[j].append(i)\n",
    "        \n",
    "\n",
    "#         def dfs(i, fa):\n",
    "#             # 带叶子的路径和，与不带叶子的路径和\n",
    "#             nonlocal ans\n",
    "#             with_leaf = price[i]\n",
    "#             with_out_leaf = 0\n",
    "\n",
    "#             for j in dct[i]:\n",
    "#                 if j != fa:\n",
    "#                     a, b = dfs(j, i)\n",
    "#                     ans = ans if ans > with_out_leaf + a else with_out_leaf + a\n",
    "#                     ans = ans if ans > with_leaf + b else with_leaf + b\n",
    "#                     with_leaf = with_leaf  if with_leaf > a + price[i] else a+ price[i]\n",
    "#                     with_out_leaf = with_out_leaf if with_out_leaf  > b+ price[i] else b+ price[i]\n",
    "\n",
    "#             return with_leaf, with_out_leaf\n",
    "\n",
    "\n",
    "#         ans = 0\n",
    "#         dfs(0, -1)\n",
    "#         return ans\n",
    "\n",
    "# ### 示例代码复写\n",
    "# from collections import defaultdict\n",
    "# class Solution:\n",
    "#     def maxOutput(self, n: int, edges: List[List[int]], price: List[int]) -> int:\n",
    "#         g=defaultdict(list)\n",
    "#         for u,v in edges:\n",
    "#             g[u].append(v)\n",
    "#             g[v].append(u)\n",
    "\n",
    "#         self.ans=0\n",
    "#         def dfs(x,fa):\n",
    "#             noleaf=0\n",
    "#             withleaf=price[x]\n",
    "#             for y in g[x]:\n",
    "#                 if y!=fa:\n",
    "#                     a,b=dfs(y,x)\n",
    "#                     self.ans=max(self.ans,a+withleaf,b+noleaf)\n",
    "#                     noleaf=max(noleaf,a+price[x])\n",
    "#                     withleaf=max(withleaf,b+price[x])\n",
    "#             return noleaf,withleaf\n",
    "\n",
    "#         dfs(0,-1)\n",
    "#         return self.ans\n",
    "\n",
    "# ### 灵茶山（1次DFS）\n",
    "# class Solution:\n",
    "#     def maxOutput(self, n: int, edges: List[List[int]], price: List[int]) -> int:\n",
    "#         g = [[] for _ in range(n)]\n",
    "#         for x, y in edges:\n",
    "#             g[x].append(y)\n",
    "#             g[y].append(x)  # 建树\n",
    "\n",
    "#         ans = 0\n",
    "#         # 返回带叶子的最大路径和，不带叶子的最大路径和\n",
    "#         def dfs(x: int, fa: int) -> (int, int):\n",
    "#             nonlocal ans\n",
    "#             max_s1 = p = price[x]\n",
    "#             max_s2 = 0\n",
    "#             for y in g[x]:\n",
    "#                 if y == fa: continue\n",
    "#                 s1, s2 = dfs(y, x)\n",
    "#                 # 前面最大带叶子的路径和 + 当前不带叶子的路径和\n",
    "#                 # 前面最大不带叶子的路径和 + 当前带叶子的路径和\n",
    "#                 ans = max(ans, max_s1 + s2, max_s2 + s1)\n",
    "#                 max_s1 = max(max_s1, s1 + p)\n",
    "#                 max_s2 = max(max_s2, s2 + p)  # 这里加上 p 是因为 x 必然不是叶子\n",
    "#             return max_s1, max_s2\n",
    "#         dfs(0, -1)\n",
    "#         return ans\n",
    "\n",
    "\n",
    "# 自叶子向根的BFS：magical-agnesibu0\n",
    "# 链接：https://leetcode.cn/problems/difference-between-maximum-and-minimum-price-sum/solution/yan-du-you-xian-sou-suo-by-magical-agnes-m3yg/\n",
    "class Solution:\n",
    "    def maxOutput(self, n: int, edges: List[List[int]], price: List[int]) -> int:\n",
    "        # 建无向图\n",
    "        g = [set() for _ in range(n)]\n",
    "        for a, b in edges:\n",
    "            g[a].add(b)\n",
    "            g[b].add(a)\n",
    "        # 额外要记录的节点信息\n",
    "        s = price.copy()  # 当前最大带叶子的路径和\n",
    "        t = [0] * n  # 当前最大不带叶子的路径和\n",
    "        # 找出图中初始叶子节点，加入待处理的集合\n",
    "        # 此处以队列为例，但换成栈、堆、有序或无序的集合都没有问题\n",
    "        q = deque(i for i, d in enumerate(g) if len(d) == 1)### 队列添加所有叶子点\n",
    "        ans = 0\n",
    "        while q:\n",
    "            a = q.popleft()  # 叶子节点\n",
    "            b = g[a].pop()  # 取上级节点\n",
    "            g[b].remove(a)  # 并删除该边\n",
    "            # 记录b节点子树中经过b的最长路径\n",
    "            # 可能是当前最大不带叶子的路径和 + 当前带叶子的路径和\n",
    "            # 或者是当前最大带叶子的路径和 + 当前不带叶子的路径和\n",
    "            ans = max(ans, s[a] + t[b], t[a] + s[b])\n",
    "            # 记录b节点的最大带叶子的路径和和不带叶子的路径和\n",
    "            s[b] = max(s[b], s[a] + price[b])\n",
    "            t[b] = max(t[b], t[a] + price[b])\n",
    "            if len(g[b]) == 1:  # 上级节点变为叶子节点\n",
    "                q.append(b)  # 则上级节点入队\n",
    "            elif not g[b]:  # 因刚删的边，已仅剩最后一个孤立节点\n",
    "                break  # 则结束\n",
    "        return ans\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "#!/usr/bin/env python3\n",
    "# -*- coding: utf-8 -*-\n",
    "# https://leetcode.cn/problems/difference-between-maximum-and-minimum-price-sum\n",
    "from typing import List\n",
    "from collections import defaultdict\n",
    "\n",
    "\n",
    "class Solution:\n",
    "    def maxOutput(self, n: int, edges: List[List[int]], price: List[int]) -> int:\n",
    "        return self.solution2(n, edges, price)\n",
    "\n",
    "    def solution1(self, n: int, edges: List[List[int]], price: List[int]) -> int:\n",
    "        \"\"\"DFS.\"\"\"\n",
    "        if n < 2:\n",
    "            return 0\n",
    "\n",
    "        degree = {}\n",
    "        nexts = defaultdict(set)  # 邻节点\n",
    "        for s, t in edges:\n",
    "            degree[s] = degree.get(s, 0) + 1\n",
    "            degree[t] = degree.get(t, 0) + 1\n",
    "            nexts[s].add(t)\n",
    "            nexts[t].add(s)\n",
    "\n",
    "        def dfs(s, pa):\n",
    "            \"\"\"s: 起始节点编号.\"\"\"\n",
    "            ans = 0\n",
    "            for n in nexts[s]:\n",
    "                if n == pa:\n",
    "                    continue\n",
    "                ans = max(ans, dfs(n, s))\n",
    "            return ans + price[s]\n",
    "\n",
    "        ans = 0\n",
    "        for i in range(n):\n",
    "            # 从 i 出发的开销\n",
    "            if degree[i] != 1:\n",
    "                continue\n",
    "            ans = max(ans, dfs(i, None) - price[i])\n",
    "\n",
    "        return ans\n",
    "\n",
    "    def solution2(self, n: int, edges: List[List[int]], price: List[int]) -> int:\n",
    "        \"\"\"树形dp.\"\"\"\n",
    "        if n < 2:\n",
    "            return 0\n",
    "\n",
    "        nexts = defaultdict(set)  # 邻节点\n",
    "        for s, t in edges:\n",
    "            nexts[s].add(t)\n",
    "            nexts[t].add(s)\n",
    "\n",
    "        def dfs(i, pa):\n",
    "            \"\"\"返回带叶子结点的最大路径和，不带叶子结点的最大路径和, 以及答案.\"\"\"\n",
    "            m1, m2, ans = price[i], 0, 0\n",
    "            for n in nexts[i]:\n",
    "                if n == pa:\n",
    "                    continue\n",
    "                cm1, cm2, cans = dfs(n, i)\n",
    "                # m1 + cm2: 前面最大带叶子结点的路径和 + 当前不带叶子结点的路径和\n",
    "                # m2 + cm1: 前面最大不带叶子结点的路径和 + 当前带叶子结点的路径和\n",
    "                ans = max(ans, cans, m1 + cm2, m2 + cm1)\n",
    "                m1 = max(m1, cm1 + price[i])\n",
    "                m2 = max(m2, cm2 + price[i])\n",
    "            return m1, m2, ans\n",
    "\n",
    "        # 以 0 作为 root\n",
    "        return dfs(0, None)[2]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def makeArrayIncreasing(self, a: List[int], b: List[int]) -> int:\n",
    "        b.sort()  # 为能二分查找，对 b 排序\n",
    "        @cache  # 缓存装饰器，避免重复计算 dfs 的结果\n",
    "        def dfs(i: int, pre: int) -> int:\n",
    "            if i < 0: return 0\n",
    "            res = dfs(i - 1, a[i]) if a[i] < pre else inf  # 不替换 a[i]\n",
    "            k = bisect_left(b, pre) - 1  # 二分查找 b 中小于 pre 的最大数的下标\n",
    "            if k >= 0:  # a[i] 替换成小于 pre 的最大数\n",
    "                res = min(res, dfs(i - 1, b[k]) + 1)\n",
    "            return res\n",
    "        ans = dfs(len(a) - 1, inf)  # 假设 a[n-1] 右侧有个无穷大的数\n",
    "        return ans if ans < inf else -1\n",
    "\n",
    "# 作者：灵茶山艾府\n",
    "# 链接：https://leetcode.cn/problems/make-array-strictly-increasing/solutions/2236095/zui-chang-di-zeng-zi-xu-lie-de-bian-xing-jhgg/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def makeSubKSumEqual(self, arr: List[int], k: int) -> int:\n",
    "        k = gcd(k, len(arr))\n",
    "        ans = 0\n",
    "        for i in range(k):\n",
    "            b = sorted(arr[i::k])\n",
    "            mid = b[len(b) // 2]\n",
    "            ans += sum(abs(x - mid) for x in b)\n",
    "        return ans\n",
    "\n",
    "# 作者：灵茶山艾府\n",
    "# 链接：https://leetcode.cn/problems/make-k-subarray-sums-equal/solutions/2203591/zhuan-huan-zhong-wei-shu-tan-xin-pei-shu-4dut/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def makeSubKSumEqual(self, arr: List[int], k: int) -> int:\n",
    "        k = gcd(k, len(arr)) #定理\n",
    "        ans = 0\n",
    "        for i in range(k):\n",
    "            b = sorted(arr[i::k])  # k 是步长\n",
    "            mid = b[len(b) // 2]\n",
    "            ans += sum(abs(x - mid) for x in b)\n",
    "        return ans\n",
    "'''\n",
    "作者：endlesscheng\n",
    "链接：https://leetcode.cn/problems/make-k-subarray-sums-equal/solution/zhuan-huan-zhong-wei-shu-tan-xin-pei-shu-4dut/\n",
    "来源：力扣（LeetCode）\n",
    "著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "#\n",
    "# @lc app=leetcode.cn id=1765 lang=python3\n",
    "#\n",
    "# [1765] 地图中的最高点\n",
    "#\n",
    "# https://leetcode.cn/problems/map-of-highest-peak/description/\n",
    "#\n",
    "# algorithms\n",
    "# Medium (66.35%)\n",
    "# Likes:    121\n",
    "# Dislikes: 0\n",
    "# Total Accepted:    24.8K\n",
    "# Total Submissions: 37.4K\n",
    "# Testcase Example:  '[[0,1],[0,0]]'\n",
    "#\n",
    "# 给你一个大小为 m x n 的整数矩阵 isWater ，它代表了一个由 陆地 和 水域 单元格组成的地图。\n",
    "# \n",
    "# \n",
    "# 如果 isWater[i][j] == 0 ，格子 (i, j) 是一个 陆地 格子。\n",
    "# 如果 isWater[i][j] == 1 ，格子 (i, j) 是一个 水域 格子。\n",
    "# \n",
    "# \n",
    "# 你需要按照如下规则给每个单元格安排高度：\n",
    "# \n",
    "# \n",
    "# 每个格子的高度都必须是非负的。\n",
    "# 如果一个格子是 水域 ，那么它的高度必须为 0 。\n",
    "# 任意相邻的格子高度差 至多 为 1 。当两个格子在正东、南、西、北方向上相互紧挨着，就称它们为相邻的格子。（也就是说它们有一条公共边）\n",
    "# \n",
    "# \n",
    "# 找到一种安排高度的方案，使得矩阵中的最高高度值 最大 。\n",
    "# \n",
    "# 请你返回一个大小为 m x n 的整数矩阵 height ，其中 height[i][j] 是格子 (i, j) 的高度。如果有多种解法，请返回 任意一个\n",
    "# 。\n",
    "# \n",
    "# \n",
    "# \n",
    "# 示例 1：\n",
    "# \n",
    "# \n",
    "# \n",
    "# \n",
    "# 输入：isWater = [[0,1],[0,0]]\n",
    "# 输出：[[1,0],[2,1]]\n",
    "# 解释：上图展示了给各个格子安排的高度。\n",
    "# 蓝色格子是水域格，绿色格子是陆地格。\n",
    "# \n",
    "# \n",
    "# 示例 2：\n",
    "# \n",
    "# \n",
    "# \n",
    "# \n",
    "# 输入：isWater = [[0,0,1],[1,0,0],[0,0,0]]\n",
    "# 输出：[[1,1,0],[0,1,1],[1,2,2]]\n",
    "# 解释：所有安排方案中，最高可行高度为 2 。\n",
    "# 任意安排方案中，只要最高高度为 2 且符合上述规则的，都为可行方案。\n",
    "# \n",
    "# \n",
    "# \n",
    "# \n",
    "# 提示：\n",
    "# \n",
    "# \n",
    "# m == isWater.length\n",
    "# n == isWater[i].length\n",
    "# 1 <= m, n <= 1000\n",
    "# isWater[i][j] 要么是 0 ，要么是 1 。\n",
    "# 至少有 1 个水域格子。\n",
    "# \n",
    "# \n",
    "#\n",
    "\n",
    "# @lc code=start\n",
    "from typing import List\n",
    "\n",
    "class Solution:\n",
    "    def highestPeak(self, isWater: List[List[int]]) -> List[List[int]]:\n",
    "        h = len(isWater)\n",
    "        w = len(isWater[0])\n",
    "        hight_res = [[-1] * w for _ in range(h)]\n",
    "        queue = []\n",
    "        for i, row in enumerate(isWater):\n",
    "            for j, val in enumerate(row):\n",
    "                if val == 1:\n",
    "                    queue.append((i, j))\n",
    "        def get_neighbor(i, j):\n",
    "            neighbor = set()\n",
    "            direct = ((-1, 0), (1, 0), (0, -1), (0, 1))\n",
    "            for n in direct:\n",
    "                i0 = i + n[0]\n",
    "                j0 = j + n[1]\n",
    "                if 0 <= i0 < h and 0 <= j0 < w:\n",
    "                    if hight_res[i0][j0] == -1:\n",
    "                        neighbor.add((i0, j0))\n",
    "            return neighbor\n",
    "\n",
    "        hight = 0\n",
    "        while queue:\n",
    "            tmp = []\n",
    "            for i in queue:\n",
    "                if hight_res[i[0]][i[1]] == -1:\n",
    "                    hight_res[i[0]][i[1]] = hight\n",
    "                tmp.extend(get_neighbor(*i))\n",
    "            queue = set(tmp)\n",
    "            hight += 1\n",
    "\n",
    "        return hight_res\n",
    "# @lc code=end\n",
    "\n",
    "case1 = [[0,1],[0,0]]\n",
    "case2 = [[0,0,1],[1,0,0],[0,0,0]]\n",
    "if __name__ == '__main__':\n",
    "    Solution().highestPeak(case2)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class TrieNode:\n",
    "    def __init__(self, val=0):\n",
    "        # 直接在前缀对应的 Trie 的每个节点存储该前缀对应的值。\n",
    "        self.val = val\n",
    "        self.next = [None] * 26\n",
    "\n",
    "class MapSum:\n",
    "\n",
    "    def __init__(self):\n",
    "        \"\"\" Trie https://leetcode.cn/problems/map-sum-pairs/solutions/1098992/jian-zhi-ying-she-by-leetcode-solution-j4xy/\n",
    "        \"\"\"\n",
    "        # root 表示 empty string\n",
    "        self.root = TrieNode()\n",
    "        # keys_last_val 记录 key last assigned value (不是前缀对应的值)\n",
    "        self.keys_last_val = dict()\n",
    "\n",
    "    def _next_index(self, char):\n",
    "        return ord(char) - ord('a')\n",
    "\n",
    "    def insert(self, key: str, val: int) -> None:\n",
    "        node = self.root\n",
    "        for char in key:\n",
    "            i = self._next_index(char)\n",
    "            if node.next[i]:\n",
    "                node = node.next[i]\n",
    "                if key in self.keys_last_val:\n",
    "                    # 如果key 已經visited before，要把 key 上一个对应的 val 先减掉 再加上 当前 val （替换掉） 才是 node 的前缀对应的值\n",
    "                    node.val = node.val - self.keys_last_val[key] + val\n",
    "                else:\n",
    "                    # 如果key 没有visited 过，直接加上\n",
    "                    node.val += val\n",
    "            else:\n",
    "                node.next[i] = TrieNode(val)\n",
    "                node = node.next[i]\n",
    "        # 更新key last assigned value\n",
    "        self.keys_last_val[key] = val\n",
    "\n",
    "    def sum(self, prefix: str) -> int:\n",
    "        # 直接在前缀树上搜索该给定的前缀对应的值即可\n",
    "        node = self.root\n",
    "        for char in prefix:\n",
    "            i = self._next_index(char)\n",
    "            if node.next[i]:\n",
    "                node = node.next[i]\n",
    "            else:\n",
    "                # 如果给定的前缀不在前缀树中，则返回 0\n",
    "                return 0\n",
    "        return node.val\n",
    "\n",
    "\n",
    "# Your MapSum object will be instantiated and called as such:\n",
    "# obj = MapSum()\n",
    "# obj.insert(key,val)\n",
    "# param_2 = obj.sum(prefix)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "# https://leetcode.cn/problems/map-sum-pairs/\n",
    "from collections import defaultdict\n",
    "\n",
    "\n",
    "class Node:\n",
    "    def __init__(self, val=None, cnt=0):\n",
    "        self.val = val\n",
    "        self.cnt = cnt\n",
    "        self.childs = defaultdict()\n",
    "        self.isEnd = False\n",
    "\n",
    "    def __repr__(self):\n",
    "        return \"val={}, cnt={}, isend={}\".format(self.val, self.cnt, self.isEnd)\n",
    "\n",
    "class Trie:\n",
    "    def __init__(self):\n",
    "        self.root = Node()\n",
    "\n",
    "    def insert(self, word, cnt):\n",
    "        cursor = self.root\n",
    "        index, length = 0, len(word)\n",
    "        for c in word:\n",
    "            # print(\"word={}, index={}, length={}\".format(word, index, length))\n",
    "            if c not in cursor.childs.keys():\n",
    "                node = Node(val=c)\n",
    "                if index == length - 1:\n",
    "                    node.isEnd = True\n",
    "                    node.cnt = cnt\n",
    "                cursor.childs[c] = node\n",
    "                cursor = node\n",
    "            else:\n",
    "                if index == length - 1:\n",
    "                    cursor.childs.get(c).isEnd = True\n",
    "                    cursor.childs.get(c).cnt = cnt\n",
    "                    # print(\"cursor.childs.get(c)=\", cursor.childs.get(c))\n",
    "                cursor = cursor.childs.get(c)\n",
    "            index += 1\n",
    "        return True\n",
    "\n",
    "    def scan(self, word):\n",
    "        cursor = self.root\n",
    "        for c in word:\n",
    "            if c not in cursor.childs.keys():\n",
    "                return 0\n",
    "            cursor = cursor.childs.get(c)\n",
    "        # print(\"cursor=\", cursor)\n",
    "\n",
    "        def comp(cursor):\n",
    "            if cursor is None:\n",
    "                return 0\n",
    "            ans = cursor.cnt if cursor.isEnd else 0\n",
    "            for child in cursor.childs.values():\n",
    "                ans += comp(child)\n",
    "            return ans\n",
    "        return comp(cursor)\n",
    "\n",
    "\n",
    "class MapSum:\n",
    "\n",
    "    def __init__(self):\n",
    "        self.trie = Trie()\n",
    "\n",
    "    def insert(self, key: str, val: int) -> None:\n",
    "        self.trie.insert(key, val)\n",
    "\n",
    "    def sum(self, prefix: str) -> int:\n",
    "        print(self.trie.root)\n",
    "        return self.trie.scan(prefix)\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    m = MapSum()\n",
    "    m.insert(\"apple\", 3)\n",
    "    print(m.sum(\"ap\"))\n",
    "    m.insert(\"app\", 2)\n",
    "    print(m.sum(\"ap\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def matchReplacement(self, s: str, sub: str, mappings: List[List[str]]) -> bool:\n",
    "        Mp = defaultdict(set)\n",
    "        ls,lsub = len(s),len(sub) \n",
    "        for a,b in mappings:\n",
    "            Mp[a].add(b)\n",
    "        for i in sub:Mp[i].add(i)\n",
    "            \n",
    "        probpos = []\n",
    "        cur = 0\n",
    "        for idx in range(ls):\n",
    "            i = s[idx]\n",
    "            if i in Mp[sub[cur]]:probpos.append(idx)\n",
    "        cur = 1\n",
    "        while probpos and cur < len(sub):\n",
    "            temp = probpos\n",
    "            probpos = []\n",
    "            for i in temp:\n",
    "                if i+1 < ls and s[i+1] in Mp[sub[cur]]:probpos.append(i+1)\n",
    "            cur += 1\n",
    "        return bool(cur == len(sub) and len(probpos))\n",
    "                \n",
    "                    \n",
    "                    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def maxChunksToSorted(self, arr: [int]) -> int:\n",
    "        stack = []\n",
    "        for num in arr:\n",
    "            if stack and num < stack[-1]: \n",
    "                head = stack.pop()\n",
    "                while stack and num < stack[-1]: stack.pop()\n",
    "                stack.append(head)\n",
    "            else: stack.append(num)\n",
    "        return len(stack)\n",
    "\n",
    "# 作者：Krahets\n",
    "# 链接：https://leetcode.cn/problems/max-chunks-to-make-sorted-ii/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "# https://leetcode-cn.com/problems/max-consecutive-ones-iii/\n",
    "class Solution:\n",
    "    def longestOnes(self, A: List[int], K: int) -> int:\n",
    "        if sum(A) + K >= len(A):\n",
    "            return len(A)\n",
    "        current_term = 1\n",
    "        current_counting = 0\n",
    "        counting_res = []\n",
    "        for i in A:\n",
    "            if i == current_term:\n",
    "                current_counting += 1\n",
    "            else:\n",
    "                counting_res.append(current_counting)\n",
    "                current_term = 1 - current_term\n",
    "                current_counting = 1\n",
    "        counting_res.append(current_counting)\n",
    "        if K == 0:\n",
    "            return max(counting_res[::2])\n",
    "        res = 0\n",
    "\n",
    "        def max_turning_range(start_pos, total):\n",
    "            zero_list = counting_res[1::2]\n",
    "            start_pos = start_pos // 2\n",
    "            end_pos = start_pos\n",
    "            remaining = total\n",
    "            while True:\n",
    "                if end_pos >= len(zero_list):\n",
    "                    return -3, remaining\n",
    "                elif remaining < zero_list[end_pos]:\n",
    "                    return 2 * end_pos - 1, remaining\n",
    "                else:\n",
    "                    remaining -= zero_list[end_pos]\n",
    "                    end_pos += 1\n",
    "\n",
    "        for i in range(1, len(counting_res), 2):\n",
    "            if counting_res[i] > K:\n",
    "                res = max(res, K + max(counting_res[i - 1], counting_res[i + 1]))\n",
    "            else:\n",
    "                end_point, remaining_k = max_turning_range(i, K)\n",
    "                if end_point < 0:\n",
    "                    new_res = sum(counting_res[i - 1:]) + remaining_k\n",
    "                    res = max(res, new_res)\n",
    "                    break\n",
    "                else:\n",
    "                    new_res = sum(counting_res[i - 1:end_point + 2]) + remaining_k\n",
    "                    res = max(res, new_res)\n",
    "        return res\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def maxPoints(self, points: List[List[int]]) -> int:\n",
    "        res = 0\n",
    "        if not points:\n",
    "            return res\n",
    "        if len(points) == 1:\n",
    "            return 1\n",
    "        for i in range(len(points)-1):\n",
    "            dic = {}\n",
    "            same = 0\n",
    "            curMax = 0\n",
    "            for j in range(i+1, len(points)):\n",
    "                if points[i] == points[j]:\n",
    "                    same += 1\n",
    "                    continue\n",
    "                if (points[i][0] - points[j][0]) == 0:\n",
    "                    rate = float('inf')\n",
    "                else:\n",
    "                    rate = (points[i][1] - points[j][1]) * 1000 / (points[i][0] - points[j][0]) * 1000\n",
    "                dic[rate] = dic.get(rate, 0) + 1\n",
    "                curMax = max(curMax, dic[rate])\n",
    "            res = max(res, curMax+same+1)\n",
    "        return res\n",
    "\n",
    "\n",
    "\n",
    "# 作者：lionKing_njuer\n",
    "# 链接：https://leetcode-cn.com/problems/max-points-on-a-line/solution/python3-zi-dian-xie-lu-149-by-lionking_njuer/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "# ### 前缀和 42/45--pass\n",
    "# class Solution:\n",
    "#     def getMaxMatrix(self, matrix: List[List[int]]) -> List[int]:\n",
    "#         res=[]\n",
    "#         ans=0\n",
    "#         m,n=len(matrix),len(matrix[0])\n",
    "#         ss=[[0]*n for _ in range(m)]\n",
    "#         for i in range(m): ### 求区域前缀和\n",
    "#             for j in range(n):\n",
    "#                 down=ss[i][j-1] if j-1>=0 else 0\n",
    "#                 right=ss[i-1][j] if i-1>=0 else 0\n",
    "#                 topleft=ss[i-1][j-1] if i-1>=0 and j-1>=0 else 0\n",
    "#                 ss[i][j]=down+right-topleft+matrix[i][j]\n",
    "#         su=float(\"-inf\")#-1\n",
    "#         res=[0]*4\n",
    "#         for t in range(m): ### 固定遍历矩形上下边\n",
    "#             # for b in range(t+1,m):\n",
    "#             for b in range(t,m):### 单行也是矩形\n",
    "#                 l=-1\n",
    "#                 for r in range(n): ### 调整矩形右边界\n",
    "#                     topleft=ss[t-1][l-1] if t-1>=0 and l>=0 else 0\n",
    "#                     right=ss[t-1][r] if t-1>=0 else 0\n",
    "#                     down=ss[b][l-1] if l>=0 else 0\n",
    "#                     cur=ss[b][r]+topleft-down-right \n",
    "#                     if cur>su:\n",
    "#                         su=cur \n",
    "#                         res[0]=t\n",
    "#                         res[1]=l+1 \n",
    "#                         res[2]=b\n",
    "#                         res[3]=r\n",
    "#                     # elif cur<0: ### 需要更新左边界case\n",
    "#                     if cur<0:\n",
    "#                         l=r \n",
    "#         return res\n",
    "\n",
    "\n",
    "# # # 作者：Ori\n",
    "# # # 链接：https://leetcode.cn/problems/max-submatrix-lcci/solution/jie-jian-da-lao-si-lu-er-wei-zhuan-yi-we-z9a4/\n",
    "# class Solution:\n",
    "#     def getMaxMatrix(self, matrix: List[List[int]]) -> List[int]:\n",
    "#         # 二维数组转变成一维数组求LIS\n",
    "#         n=len(matrix)\n",
    "#         m=len(matrix[0])\n",
    "#         ans=[0]*4 \n",
    "#         # 定义数组求和,二维转一维\n",
    "#         b=[0]*m\n",
    "#         max_sum=matrix[0][0] # 初始化\n",
    "#         r1=c1=0\n",
    "        \n",
    "#         for i in range(n):\n",
    "#             b=[0]*m # 每次循环初始化b\n",
    "#             for j in range(i,n):\n",
    "#                 sum = 0 # 定义dp[i]\n",
    "#                 for k in range(m):\n",
    "#                     b[k]+=matrix[j][k]\n",
    "#                     if sum>0:\n",
    "#                         sum+=b[k]\n",
    "#                     else:\n",
    "#                         sum=b[k] # 说明之前的和小于零需要舍弃，重新开始\n",
    "#                         r1=i\n",
    "#                         c1=k\n",
    "#                     if sum>max_sum:\n",
    "#                         max_sum=sum\n",
    "#                         ans[0]=r1\n",
    "#                         ans[1]=c1\n",
    "#                         ans[2]=j\n",
    "#                         ans[3]=k\n",
    "#         return ans\n",
    "\n",
    "# ### 示例代码\n",
    "# class Solution:\n",
    "#     def getMaxMatrix(self, matrix: List[List[int]]) -> List[int]:\n",
    "#         m, n = len(matrix), len(matrix[0])\n",
    "#         pre = [[0]*(n+1) for _ in range(m)]\n",
    "#         for i in range(m):\n",
    "#             for j in range(n):\n",
    "#                 pre[i][j+1] = pre[i][j] + matrix[i][j]\n",
    "#         max_ = matrix[0][0]\n",
    "#         ans = [0,0,0,0]\n",
    "#         for i in range(n):\n",
    "#             for j in range(i,n):\n",
    "#                 # print(i,j,pre[0][j+1] - pre[0][i])\n",
    "#                 tmp_value = 0\n",
    "#                 start_row = 0 \n",
    "#                 for k in range(m):\n",
    "#                     if tmp_value >= 0:\n",
    "#                         tmp_value += pre[k][j+1] - pre[k][i]\n",
    "#                     else:\n",
    "#                         tmp_value = pre[k][j+1] - pre[k][i]\n",
    "#                         start_row = k\n",
    "#                     if tmp_value > max_:\n",
    "#                         ans = [start_row,i,k,j]\n",
    "#                         max_ = tmp_value\n",
    "#         # print(pre,max_)\n",
    "#         return ans\n",
    "\n",
    "class Solution:\n",
    "    def getMaxMatrix(self, matrix: List[List[int]]) -> List[int]:\n",
    "        m, n = len(matrix), len(matrix[0])\n",
    "        precol=[[0]*n for _ in range(m+1)]\n",
    "        for i in range(m):\n",
    "            for j in range(n):\n",
    "                precol[i+1][j]=precol[i][j]+matrix[i][j]\n",
    "\n",
    "        su=matrix[0][0]\n",
    "        res=[0,0,0,0]\n",
    "        for t in range(m):\n",
    "            tmp=0\n",
    "            # l=0\n",
    "            for b in range(t,m):\n",
    "                cul=0\n",
    "                l=0\n",
    "                for r in range(n):\n",
    "                    if cul>0:\n",
    "                        cul+=precol[b+1][r]-precol[t][r]\n",
    "                    else:\n",
    "                        cul=precol[b+1][r]-precol[t][r]\n",
    "                        l=r\n",
    "                    if cul>su:\n",
    "                        su=cul\n",
    "                        res[0]=t\n",
    "                        res[1]=l\n",
    "                        res[2]=b\n",
    "                        res[3]=r\n",
    "        return res ### 位置要对准\n",
    "\n",
    "\n",
    "\n",
    "            \n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def maxSumSubmatrix(self, matrix: List[List[int]], k: int) -> int:\n",
    "        row , col = len(matrix) , len(matrix[0])\n",
    "        res = float(\"-inf\")\n",
    "        for left in range(col):\n",
    "            sums = [0] * row\n",
    "            for right in range(left,col):\n",
    "                for j in range(row):\n",
    "                    sums[j] += matrix[j][right]\n",
    "                lst = [0]\n",
    "                cur = 0\n",
    "                for num in sums:\n",
    "                    cur += num\n",
    "                    loc = bisect.bisect_left(lst,cur-k)\n",
    "                    if loc < len(lst):\n",
    "                        res = max(cur-lst[loc],res)\n",
    "                    bisect.insort(lst,cur)\n",
    "        return res\n",
    "\n",
    "# 作者：小周小周万事无忧\n",
    "# 链接：https://leetcode.cn/problems/max-sum-of-rectangle-no-larger-than-k/solutions/402080/ju-xing-qu-yu-bu-chao-guo-kde-zui-da-shu-zhi-he-py/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "from typing import List\n",
    "\n",
    "\n",
    "class Solution:\n",
    "    # def maxSumSubmatrix(self, matrix: List[List[int]], k: int) -> int:\n",
    "    #     # 1.Brute force solution, this will exceed the momory limit\n",
    "    #     # time complexity: (M^2*N^2)\n",
    "    #     # space complexity: (M^2*N^2)\n",
    "    #     m, n = len(matrix) + 1, len(matrix[0]) + 1\n",
    "    #     dp = [\n",
    "    #         [[[0 for _ in range(n)] for _ in range(m)] for _ in range(n)]\n",
    "    #         for _ in range(m)\n",
    "    #     ]\n",
    "\n",
    "    #     max_val = -float(\"inf\")\n",
    "\n",
    "    #     for i1 in range(1, m):\n",
    "    #         for j1 in range(1, n):\n",
    "    #             dp[i1][j1][i1][j1] = matrix[i1 - 1][j1 - 1]\n",
    "    #             for i2 in range(i1, m):\n",
    "    #                 for j2 in range(j1, n):\n",
    "    #                     dp[i1][j1][i2][j2] = (\n",
    "    #                         dp[i1][j1][i2][j2 - 1]\n",
    "    #                         + dp[i1][j1][i2 - 1][j2]\n",
    "    #                         - dp[i1][j1][i2 - 1][j2 - 1]\n",
    "    #                         + matrix[i2 - 1][j2 - 1]\n",
    "    #                     )\n",
    "\n",
    "    #                     if dp[i1][j1][i2][j2] <= k:\n",
    "    #                         max_val = max(max_val, dp[i1][j1][i2][j2])\n",
    "    #     return max_val\n",
    "\n",
    "    # def maxSumSubmatrix(self, matrix: List[List[int]], k: int) -> int:\n",
    "    #     # 2.Brute force solution with space optimization, this will exceed the time limit\n",
    "    #     # time complexity: (M^2*N^2)\n",
    "    #     # space complexity: (M*N)\n",
    "    #     m, n = len(matrix) + 1, len(matrix[0]) + 1\n",
    "    #     max_val = -float(\"inf\")\n",
    "\n",
    "    #     for i1 in range(1, m):\n",
    "    #         for j1 in range(1, n):\n",
    "    #             dp = [[0 for _ in range(n)] for _ in range(m)]\n",
    "    #             dp[i1][j1] = matrix[i1 - 1][j1 - 1]\n",
    "\n",
    "    #             for i2 in range(i1, m):\n",
    "    #                 for j2 in range(j1, n):\n",
    "    #                     dp[i2][j2] = (\n",
    "    #                         dp[i2 - 1][j2]\n",
    "    #                         + dp[i2][j2 - 1]\n",
    "    #                         - dp[i2 - 1][j2 - 1]\n",
    "    #                         + matrix[i2 - 1][j2 - 1]\n",
    "    #                     )\n",
    "\n",
    "    #                     if dp[i2][j2] <= k:\n",
    "    #                         max_val = max(max_val, dp[i2][j2])\n",
    "    #     return max_val\n",
    "\n",
    "    def maxSumSubmatrix(self, matrix: List[List[int]], k: int) -> int:\n",
    "        # 3.Brute force solution with space optimization, this will exceed the time limit\n",
    "        # time complexity: (M^2*N^2)\n",
    "        # space complexity: (M*N)\n",
    "\n",
    "        \"\"\"\n",
    "        If we enumerate the upper and lower boundaries of the rectangle and calculate the sum of elements in each column within the boundary, the original problem is transformed into the following one-dimensional problem:\n",
    "        Given an array of integers and an integer k, calculate the maximum interval sum of the array, requiring that the interval sum does not exceed k.\n",
    "        \"\"\"\n",
    "\n",
    "        def maximum_subarray_below_k(nums: List[int], k: int) -> int:\n",
    "            n = len(nums) + 1\n",
    "\n",
    "            roll_sum = roll_max = nums[0]\n",
    "            for i in range(1, n - 1):\n",
    "                if roll_sum > 0:\n",
    "                    roll_sum += nums[i]\n",
    "                else:\n",
    "                    roll_sum = nums[i]\n",
    "\n",
    "                roll_max = max(roll_max, roll_sum)\n",
    "\n",
    "            if roll_max <= k:\n",
    "                return roll_max\n",
    "\n",
    "            max_val = float(\"-inf\")\n",
    "\n",
    "            for i in range(1, n):\n",
    "                total = 0\n",
    "                for j in range(i, n):\n",
    "                    total += nums[j - 1]\n",
    "                    if total > max_val and total <= k:\n",
    "                        max_val = total\n",
    "                    if total == k:\n",
    "                        return total\n",
    "\n",
    "            return max_val\n",
    "\n",
    "        m, n = len(matrix), len(matrix[0])\n",
    "        max_val = -float(\"inf\")\n",
    "\n",
    "        # Enumerate the upper boundary\n",
    "        for i_upper in range(m):\n",
    "            row_sums = [0 for _ in range(n)]\n",
    "            # Enumerate the lower boundary\n",
    "            for i_lower in range(i_upper, m):\n",
    "                for c in range(n):\n",
    "                    row_sums[c] += matrix[i_lower][c]\n",
    "                max_val = max(max_val, maximum_subarray_below_k(row_sums, k))\n",
    "        return max_val\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def findMaxValueOfEquation(self, points: List[List[int]], k: int) -> int:\n",
    "        res = -inf\n",
    "        heap = []\n",
    "        for x, y in points:\n",
    "            while heap and x - heap[0][1] > k:\n",
    "                heappop(heap)\n",
    "            if heap:\n",
    "                res = max(res, x + y - heap[0][0])\n",
    "            heappush(heap, [x - y, x])\n",
    "        return res\n",
    "\n",
    "# 作者：力扣官方题解\n",
    "# 链接：https://leetcode.cn/problems/max-value-of-equation/solutions/2351324/man-zu-bu-deng-shi-de-zui-da-zhi-by-leet-5rbj/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def maxKelements(self, nums: List[int], k: int) -> int:\n",
    "        q = [-num for num in nums]\n",
    "        heapq.heapify(q)\n",
    "        res = 0\n",
    "        while k:\n",
    "            x = heapq.heappop(q)\n",
    "            res += abs(x)\n",
    "            heapq.heappush(q, -((-x + 2) // 3))\n",
    "            k -= 1\n",
    "        return res\n",
    "\n",
    "\n",
    "\n",
    "# 答案 - 官方题解\n",
    "# https://leetcode.cn/problems/maximal-score-after-applying-k-operations/solutions/2484596/zhi-xing-k-ci-cao-zuo-hou-de-zui-da-fen-a1jub/        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "#\n",
    "# @lc app=leetcode.cn id=2530 lang=python3\n",
    "#\n",
    "# [2530] 执行 K 次操作后的最大分数\n",
    "#\n",
    "# https://leetcode.cn/problems/maximal-score-after-applying-k-operations/description/\n",
    "#\n",
    "# algorithms\n",
    "# Medium (45.33%)\n",
    "# Likes:    40\n",
    "# Dislikes: 0\n",
    "# Total Accepted:    14.5K\n",
    "# Total Submissions: 27.2K\n",
    "# Testcase Example:  '[10,10,10,10,10]\\n5'\n",
    "#\n",
    "# 给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。你的 起始分数 为 0 。\n",
    "#\n",
    "# 在一步 操作 中：\n",
    "#\n",
    "#\n",
    "# 选出一个满足 0 <= i < nums.length 的下标 i ，\n",
    "# 将你的 分数 增加 nums[i] ，并且\n",
    "# 将 nums[i] 替换为 ceil(nums[i] / 3) 。\n",
    "#\n",
    "#\n",
    "# 返回在 恰好 执行 k 次操作后，你可能获得的最大分数。\n",
    "#\n",
    "# 向上取整函数 ceil(val) 的结果是大于或等于 val 的最小整数。\n",
    "#\n",
    "#\n",
    "#\n",
    "# 示例 1：\n",
    "#\n",
    "#\n",
    "# 输入：nums = [10,10,10,10,10], k = 5\n",
    "# 输出：50\n",
    "# 解释：对数组中每个元素执行一次操作。最后分数是 10 + 10 + 10 + 10 + 10 = 50 。\n",
    "#\n",
    "#\n",
    "# 示例 2：\n",
    "#\n",
    "#\n",
    "# 输入：nums = [1,10,3,3,3], k = 3\n",
    "# 输出：17\n",
    "# 解释：可以执行下述操作：\n",
    "# 第 1 步操作：选中 i = 1 ，nums 变为 [1,4,3,3,3] 。分数增加 10 。\n",
    "# 第 2 步操作：选中 i = 1 ，nums 变为 [1,2,3,3,3] 。分数增加 4 。\n",
    "# 第 3 步操作：选中 i = 2 ，nums 变为 [1,1,1,3,3] 。分数增加 3 。\n",
    "# 最后分数是 10 + 4 + 3 = 17 。\n",
    "#\n",
    "#\n",
    "#\n",
    "#\n",
    "# 提示：\n",
    "#\n",
    "#\n",
    "# 1 <= nums.length, k <= 10^5\n",
    "# 1 <= nums[i] <= 10^9\n",
    "#\n",
    "#\n",
    "#\n",
    "\n",
    "import heapq\n",
    "from math import ceil\n",
    "from typing import List\n",
    "\n",
    "from sortedcontainers import SortedDict\n",
    "\n",
    "def _get_max(nums):\n",
    "    maxidx, maxval = -1, 0\n",
    "    for idx, num in enumerate(nums):\n",
    "        if num > maxval:\n",
    "            maxidx = idx\n",
    "            maxval = num\n",
    "    return maxidx, maxval\n",
    "\n",
    "class Solution:\n",
    "    def maxKelements(self, nums: List[int], k: int) -> int:\n",
    "        # h = [-num for num in nums]\n",
    "        # heapq.heapify(h)\n",
    "        for idx, num in enumerate(nums):\n",
    "            nums[idx] = -num\n",
    "        heapq.heapify(nums)\n",
    "\n",
    "        res = 0\n",
    "        for _ in range(k):\n",
    "            maxval = -heapq.heappop(nums)\n",
    "            res += maxval\n",
    "            heapq.heappush(nums, -((maxval + 2)//3))\n",
    "        return res\n",
    "\n",
    "    # def maxKelements(self, nums: List[int], k: int) -> int:\n",
    "    #     d = SortedDict()\n",
    "    #     for num in nums:\n",
    "    #         d[num] = d.get(num, 0) + 1\n",
    "\n",
    "    #     res = 0\n",
    "    #     for _ in range(k):\n",
    "    #         maxval, cnt = d.peekitem()\n",
    "    #         res += maxval\n",
    "    #         if cnt == 1:\n",
    "    #             d.popitem()\n",
    "    #         else:\n",
    "    #             d[maxval] -= 1\n",
    "    #         maxval = ceil(maxval / 3)\n",
    "    #         d[maxval] = d.get(maxval, 0) + 1\n",
    "    #     return res\n",
    "\n",
    "\n",
    "    # def maxKelements(self, nums: List[int], k: int) -> int:\n",
    "    #     res = 0\n",
    "    #     for _ in range(k):\n",
    "    #         maxidx, maxval =  _get_max(nums)\n",
    "    #         res += maxval\n",
    "    #         nums[maxidx] = ceil(maxval / 3)\n",
    "    #     return res\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "class Solution:\n",
    "    def getMaxGridHappiness(self, m: int, n: int, I: int, E: int) -> int:\n",
    "        # 参考 fdvoid0:https://leetcode.cn/problems/maximize-grid-happiness/solutions/2318413/python3dong-tai-gui-hua-di-gui-ji-yi-hua-90h6/\n",
    "\n",
    "        # 我们假设左上角已算好,利用左上角计算当前一个格子的数字\n",
    "        # dfs(idx) 由 dfs(idx-1) 以及idx右侧格子,idx下侧格子, 剩余内向,外向总数 决定\n",
    "        # 所以从右下角开始递归 DFS(第idx个格子, 后续n个格子组, 内向总数, 外向总数)\n",
    "        # (相邻的两个人总共发生的变化)\n",
    "        #  己方格子\\对方格子:空   内            外\n",
    "        #  空                +0   +0            +0 \n",
    "        #  内                +0   2*inL         inL+exL\n",
    "        #  外                +0   inL+exL       2*exL\n",
    "        face2face = [\n",
    "            [0,0,0],\n",
    "            [0,-30*2, -30+20],\n",
    "            [0,-30+20,+20+20]\n",
    "        ]\n",
    "        # 后续n个格子组状态存放在 nxt = [.........]\n",
    "        @cache\n",
    "        def dfs(idx, nxt, I,E):\n",
    "            ans=0\n",
    "            if idx==-1:  #一个格子都不填\n",
    "                return 0\n",
    "            for val in [0,1,2]:\n",
    "                right=nxt[0] if idx%n != n-1 else 0\n",
    "                bottom=nxt[-1] if idx//n != m-1 else 0\n",
    "                face=face2face[val][right]+face2face[val][bottom]\n",
    "                newnxt = (val,)+nxt[:-1]\n",
    "                if val==0:\n",
    "                    ans=max(ans,dfs(idx-1, newnxt, I, E) + face)\n",
    "                elif val==1 and I-1>=0:\n",
    "                    ans=max(ans,120+dfs(idx-1, newnxt, I-1, E) + face)\n",
    "                elif val==2 and E-1>=0:\n",
    "                    ans=max(ans,40+dfs(idx-1, newnxt, I, E-1) + face)\n",
    "            return ans\n",
    "        return dfs(m*n-1, tuple([0]*n), I ,E)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "# dp. game problem\n",
    "class Solution:\n",
    "    def PredictTheWinner(self, nums: List[int]) -> bool:\n",
    "        n = len(nums)\n",
    "        pre_sum = [0]\n",
    "        for num in nums:\n",
    "            pre_sum.append(pre_sum[-1] + num)\n",
    "        \n",
    "        dp = [[0] * n for _ in range(n)]\n",
    "        # initialize\n",
    "        for i in range(n):\n",
    "            dp[i][i] = nums[i]\n",
    "        for i in range(1, n):\n",
    "            dp[i-1][i] = max(nums[i-1], nums[i])\n",
    "        # dp\n",
    "        for j in range(2, n):\n",
    "            for i in range(j - 2, -1, -1):\n",
    "                dp[i][j] = max(pre_sum[j+1] - pre_sum[i+1] - dp[i+1][j] + nums[i] \\\n",
    "                , pre_sum[j] - pre_sum[i] - dp[i][j-1] + nums[j])\n",
    "        \n",
    "        return pre_sum[n] <= 2 * dp[0][n-1]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def primeSubOperation(self, nums: List[int]) -> bool:\n",
    "        \"\"\"\n",
    "        首先构造质数数组，存放范围内的所有质数，采用埃氏筛的方法，具体参见 204 题：计数质数（https://leetcode.cn/problems/count-primes/submissions/479534809/）\n",
    "        题目要求严格递增，反向遍历，如果前一个数字大于当前数字，那么前一个数字需要减小，至少要减去二者的差\n",
    "        再通过二分查找在质数数组中找到大于前一个数字和当前数字之差的第一个数，判断他是否超过前一个数字，超过了答案不成立，直接返回 false，没超过就减去后继续遍历\n",
    "        \"\"\"\n",
    "\n",
    "        # 只有一个元素已经满足题意\n",
    "        if len(nums) == 1:\n",
    "            \n",
    "            return True\n",
    "\n",
    "        # 创建一个小于 max(nums) 的质数数组\n",
    "        prime_nums = self.get_prime_nums(max(nums))\n",
    "\n",
    "        # 质数数组不存在\n",
    "        if len(prime_nums) == 0:\n",
    "            \n",
    "            return False\n",
    "\n",
    "        for i in range(len(nums) - 1, 0, -1):\n",
    "\n",
    "            if nums[i - 1] >= nums[i]:\n",
    "\n",
    "                p = self.binary_search(prime_nums, nums[i - 1] - nums[i])\n",
    "\n",
    "                if p == -1 or p >= nums[i - 1]:\n",
    "\n",
    "                    return False\n",
    "\n",
    "                else:\n",
    "\n",
    "                    nums[i - 1] -= p\n",
    "\n",
    "        return True\n",
    "\n",
    "\n",
    "    def get_prime_nums(self, num):\n",
    "        \"\"\"\n",
    "        埃氏筛方法创建比 num 小的质数数组\n",
    "        \"\"\"\n",
    "\n",
    "        # is_prime[i] 表示数 i 是不是质数，如果是质数则为 1，否则为 0\n",
    "        is_prime = [1 for _ in range(num)]\n",
    "\n",
    "        prime_nums = []\n",
    "\n",
    "        for i in range(2, num):\n",
    "\n",
    "            if is_prime[i] == 1:\n",
    "\n",
    "                prime_nums.append(i)\n",
    "\n",
    "                if i * i < num:\n",
    "\n",
    "                    for j in range(i * i, num, i):\n",
    "\n",
    "                        is_prime[j] = 0\n",
    "\n",
    "        return prime_nums\n",
    "\n",
    "    def binary_search(self, nums, target):\n",
    "        \"\"\"\n",
    "        二分查找，找到比 target 大的第一个数\n",
    "        \"\"\"\n",
    "\n",
    "        if target >= max(nums):\n",
    "            return -1\n",
    "\n",
    "        left, right = 0, len(nums) - 1\n",
    "\n",
    "        while left <= right:\n",
    "\n",
    "            mid = (left + right) // 2\n",
    "\n",
    "            if(nums[mid] <= target):\n",
    "\n",
    "                left = mid + 1\n",
    "\n",
    "            else:\n",
    "\n",
    "                right = mid - 1\n",
    "\n",
    "        return nums[left]\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "import threading \n",
    "class FooBar:\n",
    "    def __init__(self, n):\n",
    "        self.n = n\n",
    "        self.s1=threading.Semaphore(1)\n",
    "        self.s2=threading.Semaphore(0)\n",
    "\n",
    "\n",
    "    def foo(self, printFoo: 'Callable[[], None]') -> None:\n",
    "        \n",
    "        for i in range(self.n):\n",
    "            \n",
    "            # printFoo() outputs \"foo\". Do not change or remove this line.\n",
    "            self.s1.acquire()\n",
    "            printFoo()\n",
    "            self.s2.release()\n",
    "\n",
    "\n",
    "    def bar(self, printBar: 'Callable[[], None]') -> None:\n",
    "        \n",
    "        for i in range(self.n):\n",
    "            \n",
    "            # printBar() outputs \"bar\". Do not change or remove this line.\n",
    "            self.s2.acquire()\n",
    "            printBar()\n",
    "            self.s1.release()\n",
    "\n",
    "# 作者：p瓜皮\n",
    "# 链接：https://leetcode.cn/problems/print-foobar-alternately/solutions/79145/python3-xin-hao-liang-pvcao-zuo-by-xjcai/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "import threading \n",
    "class FooBar:\n",
    "    def __init__(self, n):\n",
    "        self.n = n\n",
    "        self.s1=threading.Semaphore(1)\n",
    "        self.s2=threading.Semaphore(0)\n",
    "\n",
    "\n",
    "    def foo(self, printFoo: 'Callable[[], None]') -> None:\n",
    "        \n",
    "        for i in range(self.n):\n",
    "            \n",
    "            # printFoo() outputs \"foo\". Do not change or remove this line.\n",
    "            self.s1.acquire()\n",
    "            printFoo()\n",
    "            self.s2.release()\n",
    "\n",
    "\n",
    "    def bar(self, printBar: 'Callable[[], None]') -> None:\n",
    "        \n",
    "        for i in range(self.n):\n",
    "            \n",
    "            # printBar() outputs \"bar\". Do not change or remove this line.\n",
    "            self.s2.acquire()\n",
    "            printBar()\n",
    "            self.s1.release()\n",
    "\n",
    "# 作者：p瓜皮\n",
    "# 链接：https://leetcode.cn/problems/print-foobar-alternately/solutions/79145/python3-xin-hao-liang-pvcao-zuo-by-xjcai/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "# https://leetcode-cn.com/problems/print-in-order/solution/1114-an-xu-da-yin-python3de-5chong-jie-fa-by-tuotu/\n",
    "\n",
    "# ###while 循环法\n",
    "# class Foo:\n",
    "#     def __init__(self):\n",
    "#         self.t=0\n",
    "\n",
    "#     def first(self, printFirst: 'Callable[[], None]') -> None:\n",
    "        \n",
    "#         # printFirst() outputs \"first\". Do not change or remove this line.\n",
    "#         printFirst()\n",
    "#         self.t=1\n",
    "\n",
    "\n",
    "#     def second(self, printSecond: 'Callable[[], None]') -> None:\n",
    "#         while self.t!=1:\n",
    "#             pass\n",
    "#         # printSecond() outputs \"second\". Do not change or remove this line.\n",
    "#         printSecond()\n",
    "#         self.t=2\n",
    "\n",
    "\n",
    "#     def third(self, printThird: 'Callable[[], None]') -> None:\n",
    "#         while self.t!=2:\n",
    "#             pass\n",
    "#         # printThird() outputs \"third\". Do not change or remove this line.\n",
    "#         printThird()\n",
    "\n",
    "\n",
    "\n",
    "# ###condition 条件对象法\n",
    "# import threading\n",
    "# class Foo:\n",
    "#     def __init__(self):\n",
    "#         self.c=threading.Condition()\n",
    "#         self.t=0\n",
    "\n",
    "#     def first(self, printFirst: 'Callable[[], None]') -> None:\n",
    "        \n",
    "#         # printFirst() outputs \"first\". Do not change or remove this line.\n",
    "#         self.res(0,printFirst)\n",
    "\n",
    "\n",
    "#     def second(self, printSecond: 'Callable[[], None]') -> None:\n",
    "#         # printSecond() outputs \"second\". Do not change or remove this line.\n",
    "#         self.res(1,printSecond)\n",
    "\n",
    "\n",
    "#     def third(self, printThird: 'Callable[[], None]') -> None:\n",
    "#         # printThird() outputs \"third\". Do not change or remove this line.\n",
    "#         self.res(2,printThird)\n",
    "\n",
    "#     def res(self,val,func):\n",
    "#         with self.c:\n",
    "#             self.c.wait_for(lambda: val==self.t) #参数是函数对象，返回值是bool类型\n",
    "#             func()\n",
    "#             self.t+=1\n",
    "#             self.c.notify_all()\n",
    "\n",
    "\n",
    "###Lock 锁对象法\n",
    "# import threading\n",
    "# class Foo:\n",
    "#     def __init__(self):\n",
    "#         self.l1=threading.Lock()\n",
    "#         self.l1.acquire()\n",
    "#         self.l2=threading.Lock()\n",
    "#         self.l2.acquire()\n",
    "\n",
    "\n",
    "#     def first(self, printFirst: 'Callable[[], None]') -> None:\n",
    "        \n",
    "#         # printFirst() outputs \"first\". Do not change or remove this line.\n",
    "#         printFirst()\n",
    "#         self.l1.release()\n",
    "\n",
    "\n",
    "#     def second(self, printSecond: 'Callable[[], None]') -> None:\n",
    "#         # printSecond() outputs \"second\". Do not change or remove this line.\n",
    "#         self.l1.acquire()\n",
    "#         printSecond()\n",
    "#         self.l2.release()\n",
    "\n",
    "\n",
    "#     def third(self, printThird: 'Callable[[], None]') -> None:\n",
    "#         # printThird() outputs \"third\". Do not change or remove this line.\n",
    "#         self.l2.acquire()\n",
    "#         printThird()\n",
    "\n",
    "\n",
    "####队列法1\n",
    "import queue\n",
    "class Foo:\n",
    "    def __init__(self):\n",
    "        self.q1=queue.Queue()\n",
    "        self.q2=queue.Queue()\n",
    "\n",
    "    def first(self,printFirst):\n",
    "        printFirst()\n",
    "        self.q1.put(0)\n",
    "\n",
    "    def second(self,printSecond):\n",
    "        self.q1.get()\n",
    "        printSecond()\n",
    "        self.q2.put(0)\n",
    "\n",
    "    def third(self,printThird):\n",
    "        self.q2.get()\n",
    "        printThird()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    import math\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        n = sum(balls) // 2\n",
    "        k = len(balls)\n",
    "        def combine(x,y):\n",
    "            return math.factorial(x) // (math.factorial(y) * math.factorial(x-y))\n",
    "        def dfs(i,j,z):\n",
    "            if i == k:\n",
    "                return int(j == n and z == 0)\n",
    "            rtn = dfs(i+1,j,z-1) + dfs(i+1,j+balls[i],z+1)\n",
    "            for p in range(1,balls[i]):\n",
    "                rtn += dfs(i+1,j+p,z) * comb(balls[i],p)\n",
    "            return rtn\n",
    "        ans = dfs(0,0,0)\n",
    "        # print(ans,comb(2*n,n))\n",
    "        return ans / comb(2*n,n)\n",
    "\n",
    "        \n",
    "\n",
    "                        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        k=len(balls)\n",
    "        n=0\n",
    "        for i in balls:\n",
    "            n=n+i\n",
    "        n=int (n/2)\n",
    "        hal_ball=[0 for _ in range(k)]\n",
    "        \n",
    "\n",
    "        def c(n,m):\n",
    "           return math.factorial(n)/(math.factorial(m)*math.factorial(n-m))\n",
    "\n",
    "\n",
    "\n",
    "        def dfs(i:int,hal_ball: List[int],cnt:int):\n",
    "            if i==k:\n",
    "                if cnt!=0:\n",
    "                    return 0\n",
    "                x=0\n",
    "                y=0\n",
    "                res=1\n",
    "                for j in range(len(hal_ball)):\n",
    "                    \n",
    "                    if hal_ball[j]==0:\n",
    "                        x=x+1\n",
    "                    elif hal_ball[j]==balls[j]:\n",
    "                        y=y+1\n",
    "                if x==y:\n",
    "                    for j in range(len(hal_ball)):\n",
    "                        res=res*c(balls[j],hal_ball[j])\n",
    "                    return res\n",
    "                else:\n",
    "                    return 0\n",
    "            ans=0\n",
    "            tmp=min(cnt,balls[i])\n",
    "\n",
    "            for j in range(tmp+1):\n",
    "                hal_ball[i]=j\n",
    "                ans+=dfs(i+1,hal_ball,cnt-j)\n",
    "            return ans\n",
    "\n",
    "        return dfs(0,hal_ball,n)/c(2*n,n)\n",
    "        \n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        k = len(balls)\n",
    "        n = sum(balls) // 2\n",
    "\n",
    "        @lru_cache(None)\n",
    "        def dfs(idx, re, c):\n",
    "            if idx == k:\n",
    "                return re == 0 and c == 0\n",
    "            \n",
    "            res = 0\n",
    "            for i in range(min(balls[idx], re) + 1):\n",
    "                t = c\n",
    "                if i == 0:\n",
    "                    t -= 1\n",
    "                elif i == balls[idx]:\n",
    "                    t += 1\n",
    "                res += dfs(idx + 1, re - i, t) * comb(balls[idx], i)\n",
    "            return res\n",
    "\n",
    "        ans = dfs(0, n, 0)\n",
    "        dfs.cache_clear()\n",
    "        return ans / comb(2 * n, n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def combine(x, y):#组合数\n",
    "            return math.factorial(x) // math.factorial(y) // math.factorial(x - y)\n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        #@lru_cache(None)\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t + 1) + dfs(i + 1, c + balls[i], t - 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * combine(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / combine(s, s // 2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        s=sum(balls)\n",
    "        color=len(balls)\n",
    "\n",
    "        total=comb(s,s//2)\n",
    "        res=0\n",
    "        def dfs(i,b,t):\n",
    "            if i==color:\n",
    "                if t == 0 and b == s // 2:\n",
    "                    return 1\n",
    "                else:\n",
    "                    return 0\n",
    "            res=dfs(i+1,b,t-1)+dfs(i+1,b+balls[i],t+1)\n",
    "            for j in range(1,balls[i]):\n",
    "                res+=dfs(i+1,b+j,t)*comb(balls[i],j)\n",
    "            return res\n",
    "        res=dfs(0,0,0)\n",
    "        return res/total\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "import math\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "\n",
    "\n",
    "        sum_balls = sum(balls)\n",
    "        color_num = len(balls)\n",
    "\n",
    "        def DFS(i, c, t): \n",
    "            # i: color, \n",
    "            # c: # of balls, \n",
    "            # t: even index \n",
    "\n",
    "            if i== color_num:\n",
    "                return int(t==0 and c==(sum_balls//2) )\n",
    "            if c>(sum_balls//2):\n",
    "                return 0 \n",
    "\n",
    "            res = DFS(i+1,c,t-1)+DFS(i+1,c+balls[i],t+1)\n",
    "            for j in range(1,balls[i]):\n",
    "                res += DFS(i+1,c+j,t)*math.comb(balls[i], j)\n",
    "            return res \n",
    "        \n",
    "        res = DFS(0,0,0)\n",
    "        return res/math.comb(sum_balls, sum_balls//2)\n",
    "\n",
    "\n",
    "        \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def getRes(lst):\n",
    "            res = 1\n",
    "            for x, y in zip(lst, balls):\n",
    "                res *= comb(y, x)\n",
    "            return res\n",
    "        ans = 0\n",
    "        tmp = 0\n",
    "        def dfs(idx, tmp1, tmp2):\n",
    "            nonlocal ans, tmp\n",
    "            if idx == len(balls):\n",
    "                if sum(tmp1) == sum(tmp2):\n",
    "                    methods = getRes(tmp1)\n",
    "                    if tmp1.count(0) == tmp2.count(0):\n",
    "                        ans += methods\n",
    "                    tmp += methods\n",
    "                return \n",
    "            for i in range(balls[idx]+1):\n",
    "                tmp1.append(i)\n",
    "                tmp2.append(balls[idx]-i)\n",
    "                dfs(idx+1, tmp1, tmp2)\n",
    "                tmp1.pop()\n",
    "                tmp2.pop()\n",
    "        dfs(0, [], [])\n",
    "        return ans / tmp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        # 计算组合排列个数，长度为total，每种颜色球个数为arr\n",
    "        def compute(total, arr):\n",
    "            res = 1\n",
    "            for num in arr:\n",
    "                res *= math.comb(total, num)\n",
    "                total -= num\n",
    "            return res\n",
    "\n",
    "        # 查看其是否符合条件\n",
    "        def check():\n",
    "            if sum(choose) != n:\n",
    "                return 0\n",
    "            if color1 != color2:\n",
    "                return 0\n",
    "            return compute(n, choose) * compute(n, [balls[i] - choose[i] for i in range(k)])\n",
    "\n",
    "        # 深搜进行数量枚举\n",
    "        def dfs(i):\n",
    "            nonlocal count, color1, color2, pre\n",
    "            # 剪枝\n",
    "            if pre > n:\n",
    "                return\n",
    "            if i == k:\n",
    "                count += check()\n",
    "                return\n",
    "            for j in range(balls[i] + 1):\n",
    "                # 回溯\n",
    "                choose.append(j)\n",
    "                color1 += 1 if j > 0 else 0\n",
    "                color2 += 1 if j < balls[i] else 0\n",
    "                dfs(i + 1)\n",
    "                color1 -= 1 if j > 0 else 0\n",
    "                color2 -= 1 if j < balls[i] else 0\n",
    "                choose.pop()\n",
    "            return\n",
    "\n",
    "        k = len(balls)\n",
    "        total = sum(balls)\n",
    "        n = total // 2\n",
    "        # 颜色数量与组合种数\n",
    "        count = color1 = color2 = 0\n",
    "        choose = []\n",
    "        pre = 0\n",
    "        dfs(0)\n",
    "        return count / compute(total, balls)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def perm(nums: List[int]) -> int:\n",
    "            return factorial(sum(nums)) // prod([factorial(k) for k in nums])\n",
    "        def solve(nums: List[int], i: int) -> int:\n",
    "            if i == n:\n",
    "                if sum(nums) != m: return 0\n",
    "                n1 = nums\n",
    "                n2 = [balls[i] - nums[i] for i in range(n)]\n",
    "                k1 = sum(k != 0 for k in n1)\n",
    "                k2 = sum(k != 0 for k in n2)\n",
    "                return perm(n1) * perm(n2) if k1 == k2 else 0\n",
    "            num = 0\n",
    "            for k in range(balls[i] + 1):\n",
    "                nums[i] = k\n",
    "                num += solve(nums, i + 1)\n",
    "            return num\n",
    "        m = sum(balls) >> 1\n",
    "        n = len(balls)\n",
    "        return solve([0] * n, 0) / perm(balls)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def getRes(tmp): \n",
    "            res=1\n",
    "            for (x, y) in zip(tmp, balls):\n",
    "                res*=math.comb(y, x)\n",
    "            return res\n",
    "        ans,tmp=0,0\n",
    "        def dfs(idx, tmp0, tmp1):\n",
    "            nonlocal ans,tmp\n",
    "            if idx==len(balls):\n",
    "                if sum(tmp0)==sum(tmp1):\n",
    "                    methods=getRes(tmp0)\n",
    "                    if tmp0.count(0)==tmp1.count(0):\n",
    "                        ans+=methods\n",
    "                    tmp+=methods\n",
    "            else:\n",
    "                for i in range(balls[idx]+1):\n",
    "                    tmp0.append(balls[idx]-i)\n",
    "                    tmp1.append(i)\n",
    "                    dfs(idx+1, tmp0, tmp1)\n",
    "                    tmp0.pop()\n",
    "                    tmp1.pop()\n",
    "\n",
    "        dfs(0, [], [])\n",
    "        return ans/tmp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def fact(self, n):\n",
    "        ans = 1\n",
    "        for i in range (1, n + 1):\n",
    "            ans *= i\n",
    "        return ans\n",
    "\n",
    "    def get(self, balls):\n",
    "        tot = self.fact(sum(balls))\n",
    "        for b in balls:\n",
    "            if b:\n",
    "                tot /= self.fact(b)\n",
    "        return tot\n",
    "\n",
    "    def dfs(self, u, balls, left, right, ts, ls, rs):\n",
    "        if ls * 2 > ts or rs * 2 > ts: return 0\n",
    "        if u == len(balls):\n",
    "            l = 0\n",
    "            r = 0\n",
    "            for x in left:\n",
    "                if x:\n",
    "                    l += 1\n",
    "            for x in right:\n",
    "                if x:\n",
    "                    r += 1\n",
    "            if l != r: return 0\n",
    "            return self.get(left) * self.get(right)\n",
    "        \n",
    "        res = 0\n",
    "        for i in range(balls[u] + 1):\n",
    "            left[u] = i\n",
    "            right[u] = balls[u] - i\n",
    "            res += self.dfs(u + 1, balls, left, right, ts, ls + left[u], rs + right[u])\n",
    "        return res\n",
    "\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        l = [0 for i in range(len(balls))]\n",
    "        r = [0 for i in range(len(balls))]\n",
    "        return self.dfs(0, balls, l, r, sum(balls), 0, 0) / self.get(balls)\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def getRes(lst):\n",
    "            res = 1\n",
    "            for x, y in zip(lst, balls):\n",
    "                res *= comb(y, x)\n",
    "            return res\n",
    "        ans = 0\n",
    "        tmp = 0\n",
    "        def dfs(idx, tmp1, tmp2):\n",
    "            nonlocal ans, tmp\n",
    "            if idx == len(balls):\n",
    "                if sum(tmp1) == sum(tmp2):\n",
    "                    methods = getRes(tmp1)\n",
    "                    if tmp1.count(0) == tmp2.count(0):\n",
    "                        ans += methods\n",
    "                    tmp += methods\n",
    "                return \n",
    "            for i in range(balls[idx]+1):\n",
    "                tmp1.append(i)\n",
    "                tmp2.append(balls[idx]-i)\n",
    "                dfs(idx+1, tmp1, tmp2)\n",
    "                tmp1.pop()\n",
    "                tmp2.pop()\n",
    "        dfs(0, [], [])\n",
    "        return ans / tmp\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "from math import comb\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        total = sum(balls)\n",
    "        color = len(balls)\n",
    "        half = total // 2\n",
    "        all_ways = comb(total, half)\n",
    "        \n",
    "        # 计算颜色分配的可能性\n",
    "        def dfs(idx, left, right, color_left, color_right):\n",
    "            if left > half or right > half:\n",
    "                return 0\n",
    "            if idx == color:\n",
    "                if left == right and color_left == color_right:\n",
    "                    return comb(total - left, half - left)\n",
    "                return 0\n",
    "            ways = 0\n",
    "            for i in range(balls[idx] + 1):\n",
    "                ways += dfs(idx + 1, left + i, right + balls[idx] - i, color_left + (i > 0), color_right + (i < balls[idx])) * comb(balls[idx], i)\n",
    "            return ways\n",
    "        \n",
    "        good_ways = dfs(0, 0, 0, 0, 0)\n",
    "        \n",
    "        return good_ways / all_ways\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "\n",
    "        n = len(balls)\n",
    "        s = sum(balls)\n",
    "        @cache\n",
    "        def dfs(idx, cnt, diff):\n",
    "            if idx == n:\n",
    "                return int(diff == 0 and cnt == s // 2)\n",
    "            \n",
    "            res = dfs(idx + 1, cnt, diff - 1) + dfs(idx + 1, cnt + balls[idx], diff + 1)\n",
    "\n",
    "            for j in range(1, balls[idx]):\n",
    "                res += dfs(idx + 1, cnt + j, diff) * comb(balls[idx], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / comb(s, s // 2)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "from math import comb\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        total = sum(balls)\n",
    "        color = len(balls)\n",
    "        half = total // 2\n",
    "        all_ways = comb(total, half)\n",
    "        \n",
    "        # 计算颜色分配的可能性\n",
    "        def dfs(idx, left, right, color_left, color_right):\n",
    "            if left > half or right > half:\n",
    "                return 0\n",
    "            if idx == color:\n",
    "                if left == right and color_left == color_right:\n",
    "                    return comb(total - left, half - left)\n",
    "                return 0\n",
    "            ways = 0\n",
    "            for i in range(balls[idx] + 1):\n",
    "                ways += dfs(idx + 1, left + i, right + balls[idx] - i, color_left + (i > 0), color_right + (i < balls[idx])) * comb(balls[idx], i)\n",
    "            return ways\n",
    "        \n",
    "        good_ways = dfs(0, 0, 0, 0, 0)\n",
    "        \n",
    "        return good_ways / all_ways"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        n, s = len(balls), sum(balls)\n",
    "        half = s >> 1\n",
    "        @cache\n",
    "        def dfs(index, num, diff):\n",
    "            if index == n:\n",
    "                return int(num == half and diff == 0)\n",
    "            # 这个颜色选择多少进入一个其中一个箱子\n",
    "            # 要么一个不选 要么全部选进其中一个箱子 这会导致颜色差距变大diff加1或者减少1\n",
    "            res = dfs(index + 1, num, diff - 1) + dfs(index + 1, num + balls[index], diff + 1)\n",
    "            for i in range(1, balls[index]):\n",
    "                res += dfs(index + 1, num + i, diff) * comb(balls[index], i)\n",
    "            return res\n",
    "        \n",
    "        ans = dfs(0, 0, 0)\n",
    "        dfs.cache_clear()\n",
    "        return ans / comb(s, half)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def comb(x, y):\n",
    "            return int(math.factorial(x) / math.factorial(y) / math.factorial(x-y))\n",
    "\n",
    "        s = sum(balls)\n",
    "        l = len(balls)\n",
    "        # @lru_cache(None)\n",
    "        def dfs(curr, count, diff):\n",
    "            if curr == l:\n",
    "                return int(diff == 0 and count == s // 2)\n",
    "            res = dfs(curr+1, count, diff+1) + dfs(curr+1, count+balls[curr], diff-1)\n",
    "            for i in range(1, balls[curr]):\n",
    "                res += dfs(curr+1, count+i, diff) * comb(balls[curr], i)\n",
    "            return res\n",
    "        \n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / comb(s, s//2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "       self.colors = len(balls)\n",
    "       self.total = 0\n",
    "       self.res = 0\n",
    "       self.target_count = sum(balls) // 2\n",
    "       self.balls = balls\n",
    "       c1, c2 = 0, 0\n",
    "       self.dfs(c1, c2, 0, 0, 1)\n",
    "       return self.res / self.total\n",
    "\n",
    "\n",
    "    def dfs(self, c1: int, c2: int, count: int, idx: int, choice: int):\n",
    "        if count > self.target_count:\n",
    "            #选多了\n",
    "            return\n",
    "        if idx == self.colors: #颜色选完了\n",
    "            if count != self.target_count:\n",
    "                return\n",
    "            self.total += choice\n",
    "            if c1 == c2:\n",
    "                self.res += choice\n",
    "            return\n",
    "        # 当前颜色球有多少个\n",
    "        ball_count = self.balls[idx]\n",
    "        # 取出 i 个球放在第一个箱子，其余的放入第二个箱子\n",
    "        for i in range(ball_count + 1):\n",
    "            c11 = c1 + (0 if i == 0 else 1)\n",
    "            c22 = c2 + (0 if i == ball_count else 1)\n",
    "            this_choice = math.factorial(ball_count) / math.factorial(i) / math.factorial(ball_count - i) \n",
    "            self.dfs(c11, c22, count + i, idx + 1, choice * this_choice)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def getRes(lst):\n",
    "            res = 1\n",
    "            for x, y in zip(lst, balls):\n",
    "                res *= comb(y, x)\n",
    "            return res\n",
    "\n",
    "        ans = 0\n",
    "        tmp = 0\n",
    "\n",
    "        def dfs(idx, tmp1, tmp2):\n",
    "            nonlocal ans, tmp\n",
    "            if idx == len(balls):\n",
    "                if sum(tmp1) == sum(tmp2):\n",
    "                    methods = getRes(tmp1)\n",
    "                    if tmp1.count(0) == tmp2.count(0):\n",
    "                        ans += methods\n",
    "                    tmp += methods\n",
    "                return \n",
    "            for i in range(balls[idx]+1):\n",
    "                tmp1.append(i)\n",
    "                tmp2.append(balls[idx]-i)\n",
    "                dfs(idx+1, tmp1, tmp2)\n",
    "                tmp1.pop()\n",
    "                tmp2.pop()\n",
    "\n",
    "        dfs(0, [], [])\n",
    "        \n",
    "        return ans / tmp\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        # def combine(x, y):#组合数\n",
    "            # return math.factorial(x) // math.factorial(y) // math.factorial(x - y)\n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        @lru_cache(None)\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t - 1) + dfs(i + 1, c + balls[i], t + 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * comb(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / comb(s, s // 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def comb(x, y):\n",
    "            return int(math.factorial(x) / math.factorial(y) / math.factorial(x-y))\n",
    "\n",
    "        s = sum(balls)\n",
    "        l = len(balls)\n",
    "        @lru_cache(None)\n",
    "        def dfs(curr, count, diff):\n",
    "            if curr == l:\n",
    "                return int(diff == 0 and count == s // 2)\n",
    "            res = dfs(curr+1, count, diff+1) + dfs(curr+1, count+balls[curr], diff-1)\n",
    "            for i in range(1, balls[curr]):\n",
    "                res += dfs(curr+1, count+i, diff) * comb(balls[curr], i)\n",
    "            return res\n",
    "        \n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / comb(s, s//2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        s = sum(balls)\n",
    "        n = len(balls)\n",
    "        total,ss = 1,s\n",
    "        for i in balls:\n",
    "            total *= comb(ss,i)\n",
    "            ss -= i\n",
    "        @cache\n",
    "        def dfs(lft,rgt,lc,rc,pos):\n",
    "            if pos == n:return 1 if lc == rc else 0\n",
    "            l = min(lft,balls[pos]);r = max(balls[pos]-lft,0);shold = min(rgt,balls[pos])\n",
    "            res = 0\n",
    "            while r <= shold:\n",
    "                res += comb(lft,l) * comb(rgt,r) * dfs(lft-l,rgt-r,lc+(1 if not r else 0),rc+(1 if not l else 0),pos+1)\n",
    "                l-=1;r+=1\n",
    "            return res\n",
    "        return dfs(s//2,s//2,0,0,0)/total\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        n, s = len(balls), sum(balls)\n",
    "        half = s >> 1\n",
    "        # @cache\n",
    "        def dfs(index, num, diff):\n",
    "            if index == n:\n",
    "                return int(num == half and diff == 0)\n",
    "            # 这个颜色选择多少进入一个其中一个箱子\n",
    "            # 要么一个不选 要么全部选进其中一个箱子 这会导致颜色差距变大diff加1或者减少1\n",
    "            res = dfs(index + 1, num, diff - 1) + dfs(index + 1, num + balls[index], diff + 1)\n",
    "            for i in range(1, balls[index]):\n",
    "                res += dfs(index + 1, num + i, diff) * comb(balls[index], i)\n",
    "            return res\n",
    "        \n",
    "        ans = dfs(0, 0, 0)\n",
    "        # dfs.cache_clear()\n",
    "        return ans / comb(s, half)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        n = sum(balls) // 2\n",
    "        m = len(balls)\n",
    "        mi_color = (m + 1) // 2\n",
    "        mx_color = m \n",
    "        ans = 0\n",
    "        perms = [1] * (2 * n + 1)\n",
    "        # for i in range(2, 2 * n + 1):\n",
    "        #     perms[i] = perms[i - 1] * i \n",
    "        \"\"\"\n",
    "        color: 颜色数限定值\n",
    "        layer: 来到第几关了（上限m）\n",
    "        L: 当前状态数组\n",
    "        rest_balls: 还能挑几个球？\n",
    "        rest_color: 还差几种颜色\n",
    "        \"\"\"\n",
    "\n",
    "        def g(L):\n",
    "            res = 1\n",
    "            for i in range(m):\n",
    "                if L[i]:\n",
    "                    res *= comb(balls[i], L[i])\n",
    "            return res \n",
    "\n",
    "        def judge(L, color):\n",
    "            tarn_L = [int((balls[i] - L[i]) > 0) for i in range(m)]\n",
    "            return sum(tarn_L) == color\n",
    "\n",
    "        def f(color, layer, L, rest_balls, rest_color):\n",
    "            nonlocal ans \n",
    "            if layer == m:\n",
    "                if rest_balls == 0 and rest_color == 0 and judge(L, color):\n",
    "                    ans += g(L)\n",
    "                    # ans += perms[n]\n",
    "                return \n",
    "            if rest_color == 0:\n",
    "                if rest_balls != 0:\n",
    "                    return \n",
    "                else:\n",
    "                    f(color, m, L + [0] * (m - layer), 0, 0)\n",
    "            else:\n",
    "                f(color, layer + 1, L + [0], rest_balls, rest_color)\n",
    "                for i in range(1, min(balls[layer], rest_balls) + 1):\n",
    "                    f(color, layer + 1, L + [i], rest_balls - i, rest_color - 1)\n",
    "        \n",
    "        for col in range(mi_color, mx_color + 1):\n",
    "            f(col, 0, [], n, col)\n",
    "        return ans / comb(2 * n, n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def getRes(lst):\n",
    "            res = 1\n",
    "            for x, y in zip(lst, balls):\n",
    "                res *= comb(y, x)\n",
    "            return res\n",
    "        ans = 0\n",
    "        tmp = 0\n",
    "        def dfs(idx, tmp1, tmp2):\n",
    "            nonlocal ans, tmp\n",
    "            if idx == len(balls):\n",
    "                if sum(tmp1) == sum(tmp2):\n",
    "                    methods = getRes(tmp1)\n",
    "                    if tmp1.count(0) == tmp2.count(0):\n",
    "                        ans += methods\n",
    "                    tmp += methods\n",
    "                return \n",
    "            for i in range(balls[idx]+1):\n",
    "                tmp1.append(i)\n",
    "                tmp2.append(balls[idx]-i)\n",
    "                dfs(idx+1, tmp1, tmp2)\n",
    "                tmp1.pop()\n",
    "                tmp2.pop()\n",
    "        dfs(0, [], [])\n",
    "        return ans / tmp\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    # 计算阶乘\n",
    "    def fact(self, n):\n",
    "        ans = 1\n",
    "        for i in range (1, n + 1):\n",
    "            ans *= i\n",
    "        return ans\n",
    "\n",
    "    # 计算可重复数排列\n",
    "    def get(self, balls):\n",
    "        tot = self.fact(sum(balls))\n",
    "        for b in balls:\n",
    "            if b:\n",
    "                tot /= self.fact(b)\n",
    "        return tot\n",
    "\n",
    "    # 暴搜每个颜色的所有可能\n",
    "    def dfs(self, u, balls, left, right, ts, ls, rs):\n",
    "        if ls * 2 > ts or rs * 2 > ts: return 0 # 不满足每个盒子放n个\n",
    "        # 所有颜色都选了\n",
    "        if u == len(balls):\n",
    "            # 统计左右颜色的个数\n",
    "            l = 0\n",
    "            r = 0\n",
    "            for x in left:\n",
    "                if x:\n",
    "                    l += 1\n",
    "            for x in right:\n",
    "                if x:\n",
    "                    r += 1\n",
    "            if l != r: return 0\n",
    "            # 对当前颜色方案计算可重复排列，比如1个color0 0个color1 2个color2，可以排列0 2 2, 2 0 2\n",
    "            return self.get(left) * self.get(right)\n",
    "        \n",
    "        # 暴搜当前balls[u]颜色，选0到balls[u]个\n",
    "        res = 0\n",
    "        for i in range(balls[u] + 1):\n",
    "            left[u] = i\n",
    "            right[u] = balls[u] - i\n",
    "            res += self.dfs(u + 1, balls, left, right, ts, ls + left[u], rs + right[u])\n",
    "        return res\n",
    "\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        l = [0 for i in range(len(balls))]\n",
    "        r = [0 for i in range(len(balls))]\n",
    "        return self.dfs(0, balls, l, r, sum(balls), 0, 0) / self.get(balls)\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "from math import comb\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        n = sum(balls) // 2\n",
    "        k = len(balls)\n",
    "        dp: list[dict[tuple[int, int, int], float]] = [dict(), dict()]\n",
    "        dp[0][0, 0, 0] = 1.0\n",
    "\n",
    "        for i, ball in enumerate(balls):\n",
    "            dp[i & 1 ^ 1].clear()\n",
    "            for (a, b, c), p in dp[i & 1].items():\n",
    "                ways = comb(2 * n - a - b, ball)\n",
    "                for ba in range(max(0, ball + b - n), min(ball, n - a) + 1):\n",
    "                    bb = ball - ba\n",
    "                    nxt_c = c + (1 if ba > 0 else 0) - (1 if bb > 0 else 0)\n",
    "                    if i - k + 1 <= nxt_c <= k - i - 1:\n",
    "                        nxt_a = a + ba\n",
    "                        nxt_b = b + bb\n",
    "                        dp[i & 1 ^ 1].setdefault((nxt_a, nxt_b, nxt_c), 0.0)\n",
    "                        dp[i & 1 ^ 1][nxt_a, nxt_b, nxt_c] += p * comb(n - a, ba) * comb(n - b, bb) / ways\n",
    "        \n",
    "        return sum(dp[k & 1].values())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "    \n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        #@lru_cache(None)\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t - 1) + dfs(i + 1, c + balls[i], t + 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * comb(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / comb(s, s // 2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def combine(x, y):#组合数\n",
    "            return math.factorial(x) // math.factorial(y) // math.factorial(x - y)\n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        @lru_cache(None)\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t + 1) + dfs(i + 1, c + balls[i], t - 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * combine(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / combine(s, s // 2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def combine(x, y):#组合数\n",
    "            return math.factorial(x) // math.factorial(y) // math.factorial(x - y)\n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        @lru_cache(None)\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t + 1) + dfs(i + 1, c + balls[i], t - 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * combine(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / combine(s, s // 2)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def combine(x, y):\n",
    "            return math.factorial(x)//math.factorial(y)//math.factorial(x-y)\n",
    "        m=sum(balls)\n",
    "        n=len(balls)\n",
    "        @cache\n",
    "        def dfs(i,c,t):\n",
    "            if c>m//2:\n",
    "                return 0\n",
    "            if i==n:\n",
    "                return int(t==0 and c==m//2)\n",
    "            res = dfs(i+1,c,t+1)+dfs(i+1,c+balls[i],t-1)\n",
    "            for j in range(1,balls[i]):\n",
    "                res+=dfs(i+1,c+j,t)*combine(balls[i],j)\n",
    "            return res\n",
    "        res=dfs(0,0,0)\n",
    "        return res/combine(m,m//2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "\n",
    "        m = len(balls)\n",
    "\n",
    "        n = sum(balls)\n",
    "\n",
    "        total = 1\n",
    "        rest = n\n",
    "        for num in balls:\n",
    "            total *= math.comb(rest, num)\n",
    "            rest -= num\n",
    "        pre = list(accumulate(balls, initial=0))\n",
    "        \n",
    "        @lru_cache(None)\n",
    "        def dfs(i, s, c1, c2):\n",
    "            if i == m:\n",
    "                return c1 == c2 and s == n//2\n",
    "            res = 0\n",
    "            t = pre[i] - s\n",
    "            for x in range(0, balls[i]+1):\n",
    "                if s + x <= n//2 and t + balls[i]-x <= n//2:\n",
    "                    res += dfs(i+1, s+x, c1+int(x>0), c2+int(x<balls[i]))*math.comb((n//2-s), x)*math.comb((n//2-t), balls[i]-x)\n",
    "\n",
    "            return res\n",
    "\n",
    "        return dfs(0, 0, 0, 0) / total"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def getRes(lst):\n",
    "            res = 1\n",
    "            for x, y in zip(lst, balls):\n",
    "                res *= comb(y, x)\n",
    "            return res\n",
    "        ans = 0\n",
    "        tmp = 0\n",
    "        def dfs(idx, tmp1, tmp2):\n",
    "            nonlocal ans, tmp\n",
    "            if idx == len(balls):\n",
    "                if sum(tmp1) == sum(tmp2):\n",
    "                    methods = getRes(tmp1)\n",
    "                    if tmp1.count(0) == tmp2.count(0):\n",
    "                        ans += methods\n",
    "                    tmp += methods\n",
    "                return \n",
    "            for i in range(balls[idx]+1):\n",
    "                tmp1.append(i)\n",
    "                tmp2.append(balls[idx]-i)\n",
    "                dfs(idx+1, tmp1, tmp2)\n",
    "                tmp1.pop()\n",
    "                tmp2.pop()\n",
    "        dfs(0, [], [])\n",
    "        return ans / tmp\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def combine(x, y):#组合数\n",
    "            return math.factorial(x) // math.factorial(y) // math.factorial(x - y)\n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        @lru_cache(None)\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t + 1) + dfs(i + 1, c + balls[i], t - 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * combine(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / combine(s, s // 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def combine(x, y):#组合数\n",
    "            return math.factorial(x) // math.factorial(y) // math.factorial(x - y)\n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        @lru_cache(None)\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t + 1) + dfs(i + 1, c + balls[i], t - 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * combine(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / combine(s, s // 2)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        N = sum(balls) >> 1\n",
    "        n = len(balls)\n",
    "        path = []\n",
    "\n",
    "        # @cache\n",
    "        def cal(path: tuple[int]):\n",
    "            res = 1\n",
    "            left = N\n",
    "            for x in path:\n",
    "                if x == 0:\n",
    "                    continue\n",
    "                res *= comb(left, x)\n",
    "                left -= x\n",
    "            return res\n",
    "\n",
    "        # @cache\n",
    "        def dfs(i: int, diff: int, cnt: int, path: tuple[int]):\n",
    "            if cnt > N:\n",
    "                return 0\n",
    "            if i == n:\n",
    "                if cnt == N and diff == 0:\n",
    "                    # print(path)\n",
    "                    other = tuple([balls[i] - x for i, x in enumerate(path)])\n",
    "                    res = cal(path) * cal(other)\n",
    "                    return res\n",
    "                return 0\n",
    "            res = 0\n",
    "            for x in range(0, balls[i] + 1):\n",
    "                d = 0\n",
    "                if x == 0:\n",
    "                    d = -1\n",
    "                if x == balls[i]:\n",
    "                    d = 1\n",
    "                res += dfs(i + 1, diff + d, cnt + x, tuple(list(path) + [x]))\n",
    "            # print(i, diff, cnt, res)\n",
    "            return res\n",
    "\n",
    "        # @cache\n",
    "        def dfs2(i: int, left: int, right: int):\n",
    "            if i == n:\n",
    "                return left == 0 and right == 0\n",
    "            if left < 0 or right < 0:\n",
    "                return 0\n",
    "            res = 0\n",
    "            for x in range(0, balls[i] + 1):\n",
    "                res += (\n",
    "                    comb(left, x)\n",
    "                    * comb(right, balls[i] - x)\n",
    "                    * dfs2(i + 1, left - x, right - (balls[i] - x))\n",
    "                )\n",
    "            return res\n",
    "\n",
    "        res = dfs(0, 0, 0, tuple(path))\n",
    "        # print(res)\n",
    "        res2 = dfs2(0, N, N)\n",
    "        # print(res2)\n",
    "        # dfs.cache_clear()\n",
    "        # dfs2.cache_clear()\n",
    "        return res / res2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def combine(x, y):#组合数\n",
    "            return math.factorial(x) // math.factorial(y) // math.factorial(x - y)\n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        @lru_cache(None)\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t + 1) + dfs(i + 1, c + balls[i], t - 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * combine(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / combine(s, s // 2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def combine(x, y):#组合数\n",
    "            return math.factorial(x) // math.factorial(y) // math.factorial(x - y)\n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        @lru_cache(None)\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t + 1) + dfs(i + 1, c + balls[i], t - 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * combine(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / combine(s, s // 2)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        ans = 0\n",
    "        k = len(balls)\n",
    "        n = sum(balls)\n",
    "\n",
    "        dp = [[0] * (n + 1) for _ in range(n + 1)]\n",
    "        dp[0][0] = 1\n",
    "        for i in range(1, n + 1):\n",
    "            for j in range(n + 1):\n",
    "                if j:\n",
    "                    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]\n",
    "                else:\n",
    "                    dp[i][j] = 1\n",
    "        \n",
    "\n",
    "        def helper(p, a, b):\n",
    "            nonlocal ans\n",
    "            if p == k:\n",
    "                ac, bc, nums = 0, 0, [0, 0]\n",
    "                for i in range(k):\n",
    "                    if a[i]:\n",
    "                        ac += 1\n",
    "                        nums[0] += a[i]\n",
    "                    if b[i]:\n",
    "                        bc += 1\n",
    "                        nums[1] += b[i]\n",
    "\n",
    "                if ac == bc and nums[0] == nums[1]:\n",
    "                    res = 1\n",
    "                    for i in range(k):\n",
    "                        res *= dp[balls[i]][a[i]]\n",
    "                    ans += res / dp[n][nums[0]]\n",
    "                return\n",
    "            \n",
    "            for cnt in range(balls[p] + 1):\n",
    "                a[p] = cnt\n",
    "                b[p] = balls[p] - cnt\n",
    "                helper(p + 1, a, b)\n",
    "\n",
    "        helper(0, [0] * k, [0] * k)\n",
    "        return ans"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def combine(x, y):#组合数\n",
    "            return math.factorial(x) // math.factorial(y) // math.factorial(x - y)\n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        @cache\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t + 1) + dfs(i + 1, c + balls[i], t - 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * combine(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / combine(s, s // 2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "    \n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        @lru_cache(None)\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t + 1) + dfs(i + 1, c + balls[i], t - 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * comb(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / comb(s, s // 2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def comb(x, y):\n",
    "            return int(math.factorial(x) / math.factorial(y) / math.factorial(x-y))\n",
    "\n",
    "        s = sum(balls)\n",
    "        l = len(balls)\n",
    "        @lru_cache(None)\n",
    "        def dfs(curr, count, diff):\n",
    "            if curr == l:\n",
    "                return int(diff == 0 and count == s // 2)\n",
    "            res = dfs(curr+1, count, diff+1) + dfs(curr+1, count+balls[curr], diff-1)\n",
    "            for i in range(1, balls[curr]):\n",
    "                res += dfs(curr+1, count+i, diff) * comb(balls[curr], i)\n",
    "            return res\n",
    "        \n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / comb(s, s//2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "\n",
    "        n = len(balls)\n",
    "        s = sum(balls)\n",
    "        @cache\n",
    "        def dfs(idx, cnt, diff):\n",
    "            if idx == n:\n",
    "                return int(diff == 0 and cnt == s // 2)\n",
    "            \n",
    "            res = dfs(idx + 1, cnt, diff + 1) + dfs(idx + 1, cnt + balls[idx], diff - 1)\n",
    "\n",
    "            for j in range(1, balls[idx]):\n",
    "                res += dfs(idx + 1, cnt + j, diff) * comb(balls[idx], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / comb(s, s // 2)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        \n",
    "        s=sum(balls)\n",
    "        l=len(balls)\n",
    "        @cache\n",
    "        def dfs(i,c,t):\n",
    "            if i==l:\n",
    "                return c==s//2 and t==0\n",
    "            res=dfs(i+1,c,t+1)+dfs(i+1,c+balls[i],t-1)\n",
    "            for j in range(1,balls[i]):\n",
    "                res+=dfs(i+1,c+j,t)*comb(balls[i],j)\n",
    "            return res\n",
    "        res=dfs(0,0,0)\n",
    "        return res/comb(s,s//2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def combine(x, y):\n",
    "            return math.factorial(x) // math.factorial(y) // math.factorial(x - y)\n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        @cache\n",
    "        def dfs(i, c, t):\n",
    "            if c>s//2:\n",
    "                return 0\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t + 1) + dfs(i + 1, c + balls[i], t - 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * combine(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / combine(s, s // 2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        N = sum(balls) >> 1\n",
    "        n = len(balls)\n",
    "\n",
    "        @cache\n",
    "        def dfs(i: int, diff: int, cnt: int):\n",
    "            if cnt > N:\n",
    "                return 0\n",
    "            if i == n:\n",
    "                return cnt == N and diff == 0\n",
    "            res = 0\n",
    "            for x in range(0, balls[i] + 1):\n",
    "                d = 0\n",
    "                if x == 0:\n",
    "                    d = -1\n",
    "                if x == balls[i]:\n",
    "                    d = 1\n",
    "                res += comb(balls[i], x) * dfs(i + 1, diff + d, cnt + x)\n",
    "\n",
    "            return res\n",
    "\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / comb(2 * N, N)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def comb(x,y):\n",
    "            return math.factorial(x)//math.factorial(y)//math.factorial(x-y)\n",
    "        s = sum(balls)\n",
    "        color_num = len(balls)\n",
    "        @lru_cache(None)\n",
    "        def dp(i, n, t):\n",
    "            '''第几个颜色，总数，分数'''\n",
    "            # 出口\n",
    "            if i == color_num:\n",
    "                return int(n==s//2 and t == 0)\n",
    "            \n",
    "            res = dp(i+1, n+balls[i], t+1) + dp(i+1, n, t-1)\n",
    "            for j in range(1,balls[i]):\n",
    "                res += dp(i+1,n + j, t ) * comb(balls[i],j)\n",
    "            return res \n",
    "        return dp(0,0,0)/comb(s,s//2)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        @cache\n",
    "        def dfs(i: int, j: int, diff: int) -> float:\n",
    "            if i >= k:\n",
    "                return 1 if j == 0 and diff == 0 else 0\n",
    "            if j < 0:\n",
    "                return 0\n",
    "            ans = 0\n",
    "            for x in range(balls[i] + 1):\n",
    "                y = 1 if x == balls[i] else (-1 if x == 0 else 0)\n",
    "                ans += dfs(i + 1, j - x, diff + y) * comb(balls[i], x)\n",
    "            return ans\n",
    "\n",
    "        n = sum(balls) >> 1\n",
    "        k = len(balls)\n",
    "        return dfs(0, n, 0) / comb(n << 1, n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        def combine(x, y):#组合数\n",
    "            return math.factorial(x) // math.factorial(y) // math.factorial(x - y)\n",
    "        s = sum(balls)#球总数\n",
    "        l = len(balls)#颜色总数\n",
    "        @lru_cache(None)\n",
    "        def dfs(i, c, t):\n",
    "            if i == l:#如果颜色选完了\n",
    "                return int(t == 0 and c == s // 2) #选了1/2的球数量且颜色无变化\n",
    "            res = dfs(i + 1, c, t - 1) + dfs(i + 1, c + balls[i], t + 1)#不选和全选组合数都为1，直接相加即可，需要更新颜色变化\n",
    "            for j in range(1, balls[i]):#其他情况，颜色无变化\n",
    "                res += dfs(i + 1, c + j, t) * combine(balls[i], j)\n",
    "            return res\n",
    "        res = dfs(0, 0, 0)\n",
    "        return res / combine(s, s // 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        m = len(balls)  # m种颜色\n",
    "        cum_balls = [0]\n",
    "        for n in balls:\n",
    "            cum_balls.append(cum_balls[-1] + n)\n",
    "        s = cum_balls[-1] // 2  # s个球\n",
    "\n",
    "        # i个颜色，第一个里面有j分类，第二个里面有k颜色，第一个里面有n个球的种类数\n",
    "        data = [[[[0] * (s + 1) for _ in range(m + 1)] for _ in range(m + 1)] for _ in range(m + 1)]\n",
    "        all_data = [[0] * (s + 1) for _ in range(m + 1)]\n",
    "\n",
    "        data[0][0][0][0] = 1\n",
    "        all_data[0][0] = 1\n",
    "        for i in range(m):\n",
    "            for j in range(m + 1):\n",
    "                for k in range(m + 1):\n",
    "                    for n in range(s + 1):\n",
    "                        if data[i][j][k][n] == 0:\n",
    "                            continue\n",
    "\n",
    "                        if n + balls[i] <= s:\n",
    "                            data[i + 1][j + 1][k][n + balls[i]] += data[i][j][k][n]\n",
    "                        data[i + 1][j][k + 1][n] += data[i][j][k][n]\n",
    "\n",
    "                        for t in range(1, balls[i]):\n",
    "                            if n + t <= s:\n",
    "                                data[i + 1][j + 1][k + 1][n + t] += data[i][j][k][n] * self.select_cnt(balls[i], t)\n",
    "                            else:\n",
    "                                break\n",
    "\n",
    "            for n in range(s + 1):\n",
    "                if all_data[i][n] == 0:\n",
    "                    continue\n",
    "                for j in range(balls[i] + 1):\n",
    "                    if n + j <= s:\n",
    "                        all_data[i + 1][n + j] += all_data[i][n] * self.select_cnt(balls[i], j)\n",
    "\n",
    "        valid_cnt = 0\n",
    "        all_cnt = all_data[-1][s]\n",
    "        for j in range(m + 1):\n",
    "            valid_cnt += data[-1][j][j][s]\n",
    "\n",
    "        return valid_cnt / all_cnt\n",
    "    \n",
    "    @lru_cache()\n",
    "    def select_cnt(self, m, n):\n",
    "        p1 = 1\n",
    "        p2 = 1\n",
    "        for t in range(n):\n",
    "            p1 *= m - t\n",
    "            p2 *= n - t\n",
    "        return p1 / p2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        n = sum(balls) // 2\n",
    "        @lru_cache(None)\n",
    "        def dfs(i, k, s1, s2):\n",
    "            if i == len(balls):\n",
    "                if s1 == s2 and k == n:\n",
    "                    return 1\n",
    "                else:\n",
    "                    return 0\n",
    "            if k > n:\n",
    "                return 0\n",
    "            res = 0\n",
    "            for j in range(balls[i] + 1):\n",
    "                if j == 0:\n",
    "                    res += dfs(i + 1, k + j, s1, s2 + 1) * comb(balls[i], j)\n",
    "                elif j == balls[i]:\n",
    "                    res += dfs(i + 1, k + j, s1 + 1, s2) * comb(balls[i], j)\n",
    "                else:\n",
    "                    res += dfs(i + 1, k + j, s1 + 1, s2 + 1) * comb(balls[i], j)\n",
    "            return res\n",
    "\n",
    "        @lru_cache(None)\n",
    "        def dfs2(i, k):\n",
    "            if i == len(balls):\n",
    "                if k == n:\n",
    "                    return 1\n",
    "                else:\n",
    "                    return 0\n",
    "            if k > n:\n",
    "                return 0\n",
    "            res = 0\n",
    "            for j in range(balls[i] + 1):\n",
    "                res += dfs2(i + 1, k + j) * comb(balls[i], j)\n",
    "            return res\n",
    "        return dfs(0, 0, 0, 0) / dfs2(0, 0)\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    '''\n",
    "    递归遍历\n",
    "    先要明白题意，题目就是要在2n个具有k种不同颜色的球中，随机抽取n个放入第一个盒子，剩下的n个放入第二个盒子，且两个盒子中不同颜色的个数相同的概率\n",
    "    在2n中选取n个的组合数为A，在n个球中选择m个放入盒子1，剩下的放入盒子2的组合数为B，我们只要判断盒子1和盒子2中颜色的种类相同，那么题目要求的总概率就是B/A，\n",
    "    而k种颜色中任何一种颜色往盒子1放多少，能正好满足两个盒子颜色种类相同的可能，根据乘法原则就是 B = b1 * b2 * b3 * ... * b3\n",
    "    '''\n",
    "    def getProbability(self, balls: List[int]) -> float:\n",
    "        ans = 0\n",
    "        k = len(balls)\n",
    "        '''\n",
    "        注意这里的n实际上就是题目中球的总数2n\n",
    "        '''\n",
    "        n = sum(balls)\n",
    "\n",
    "        '''\n",
    "        把组合数用递推的方式先算出来\n",
    "        dp[i][j]表示i个元素中取j个元素的组合数\n",
    "        '''\n",
    "        dp = [[0] * (2 * n + 1) for _ in range(2 * n + 1)]\n",
    "        dp[0][0] = 1\n",
    "        for i in range(1, 2 * n + 1):\n",
    "            for j in range(2 * n + 1):\n",
    "                if j:\n",
    "                    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]\n",
    "                else:\n",
    "                    dp[i][j] = 1\n",
    "        \n",
    "        '''\n",
    "        p：当前遍历到第几种颜色\n",
    "        a：第一个盒子中选择的颜色类型和对应的数量（数组）\n",
    "        b：第二个盒子中选择的颜色类型和对应的数量（数组）\n",
    "        '''\n",
    "        def helper(p, a, b):\n",
    "            nonlocal ans\n",
    "            '''\n",
    "            当分配到最后一种颜色，看看是否满足要求，如果满足要求就开始计算概率\n",
    "            '''\n",
    "            if p == k:\n",
    "                ac, bc, nums = 0, 0, [0, 0]\n",
    "                for i in range(k):\n",
    "                    if a[i]:\n",
    "                        ac += 1\n",
    "                        nums[0] += a[i]\n",
    "                    if b[i]:\n",
    "                        bc += 1\n",
    "                        nums[1] += b[i]\n",
    "                \n",
    "                if ac == bc and nums[0] == nums[1]:\n",
    "                    res = 1\n",
    "                    for i in range(k):\n",
    "                        '''\n",
    "                        既然当前的颜色和对应球的数量分配是合理的，那么就开始遍历计算每一种颜色分配的概率\n",
    "                        '''\n",
    "                        res *= dp[balls[i]][a[i]]\n",
    "                    '''\n",
    "                    把当前一种选择组合的概率算出来进行累加\n",
    "                    '''\n",
    "                    ans += res / dp[n][nums[0]]\n",
    "                return\n",
    "            '''\n",
    "            对应位置的颜色尝试任何一种分配方式，再递归选择下一个颜色继续分配\n",
    "            '''\n",
    "            for cnt in range(balls[p] + 1):\n",
    "                a[p] = cnt\n",
    "                b[p] = balls[p] - cnt\n",
    "                helper(p + 1, a, b)\n",
    "\n",
    "        helper(0, [0] * k, [0] * k)\n",
    "        return ans"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        a, b = 0, 0\n",
    "        locus = [[0,0]]\n",
    "        for i in command:\n",
    "            if i == 'U':\n",
    "                a += 1\n",
    "            else:\n",
    "                b += 1\n",
    "            locus.append([b, a])\n",
    "        # 假设一定能到达终点\n",
    "        circle = min(x//b, y//a)\n",
    "        if [x-b*circle, y-a*circle] not in locus:\n",
    "            return False\n",
    "        for o in obstacles:\n",
    "            i, j = o[0], o[1]\n",
    "            # 如果当前节点在目标节点右上方，直接跳过\n",
    "            if i>x or j>y:\n",
    "                continue\n",
    "            c = min(i//b, j//a)\n",
    "            if [i-b*c, j-a*c] in locus:\n",
    "                return False\n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        xx, yy = 0, 0\n",
    "        dot_list = [[0, 0]]\n",
    "        for i in command:\n",
    "            if i == 'R':\n",
    "                xx += 1\n",
    "            else:\n",
    "                yy += 1\n",
    "            dot_list.append([xx, yy])\n",
    "        xb = dot_list[-1][0]\n",
    "        yb = dot_list[-1][1]\n",
    "        for i in obstacles:\n",
    "            if i[0] > x or i[1] >y:\n",
    "                continue\n",
    "            b = min(i[0] // xb, i[1] // yb)\n",
    "            if [i[0] - b * xb, i[1] - b * yb] in dot_list:\n",
    "                return False\n",
    "        b = min(x // xb, y // yb)\n",
    "        if [x - b * xb, y - b *yb] in dot_list:\n",
    "            return True\n",
    "        else:\n",
    "            return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        temp=[0,0]\n",
    "        one_cycle=[[0,0]]\n",
    "        for elem in command:\n",
    "            if elem=='U':\n",
    "                temp[1]=temp[1]+1\n",
    "                one_cycle.append(temp[:])\n",
    "            else:\n",
    "                temp[0]=temp[0]+1\n",
    "                one_cycle.append(temp[:])\n",
    "        for elem in obstacles:\n",
    "            if (elem[0]>=x )and elem[1]>=y:\n",
    "                continue\n",
    "            all_cycle=min(elem[0]//temp[0],elem[1]//temp[1])\n",
    "            leave=[0,0]\n",
    "            leave[0]=elem[0]-all_cycle*temp[0]\n",
    "            leave[1]=elem[1]-all_cycle*temp[1]\n",
    "            if leave in one_cycle:\n",
    "                return False\n",
    "        all_cycle=min(x//temp[0],y//temp[1])\n",
    "        leave=[0,0]\n",
    "        leave[0]=x-all_cycle*temp[0]\n",
    "        leave[1]=y-all_cycle*temp[1]\n",
    "        if leave in one_cycle:\n",
    "            return True\n",
    "        else:\n",
    "            return False\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "from collections import defaultdict\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        # if x<0 or y<0:return False\n",
    "        # # obs=defaultdict(defaultdict(int))\n",
    "        # obs=defaultdict(list)\n",
    "        # for i,j in obstacles:\n",
    "        #     # obs[i][j]=1\n",
    "        #     obs[i].append(j)\n",
    "        # i,j=0,0\n",
    "        # for step in range(x+y):\n",
    "        #     if command[step%len(command)]=='U':\n",
    "        #         j+=1\n",
    "        #     elif command[step%len(command)]=='R':\n",
    "        #         i+=1\n",
    "        #     else:\n",
    "        #         continue\n",
    "        #     print(i,j)\n",
    "        #     if j in obs[i]:return False\n",
    "        #     # if [i,j] in obstacles:return False\n",
    "        #     # if obs[i][j]==1:return False\n",
    "        #     if i==x and j==y:return True\n",
    "        # return False\n",
    "        xi,yi=0,0\n",
    "        # first_circle=[]#这里还要一个【0,0】\n",
    "        first_circle=[[0,0]]\n",
    "        for c in command:\n",
    "            if c=='U':yi+=1\n",
    "            if c=='R':xi+=1\n",
    "            first_circle.append([xi,yi])#第一个循环里所有经过的轨迹\n",
    "        loops=min(x//xi,y//yi)\n",
    "        if [x-loops*xi,y-loops*yi]not in first_circle:return False\n",
    "        #判断障碍物\n",
    "        for obx,oby in obstacles:\n",
    "            loops=min(obx//xi,oby//yi)\n",
    "            if obx<=x and oby<=y and [obx-loops*xi,oby-loops*yi]in first_circle:\n",
    "                return False\n",
    "        return True\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        route = self.getRoute(command)\n",
    "        if not self.onRoute(route, [x, y]):\n",
    "            return False\n",
    "        for obstacle in obstacles:\n",
    "            if (obstacle[0] >= x and obstacle[1] >= y) or (obstacle[0] == x and obstacle[1] == y):\n",
    "                continue\n",
    "            if self.onRoute(route, obstacle):\n",
    "                return False\n",
    "        return True\n",
    "    \n",
    "    def onRoute(self, route: List[List[int]], pos: List[int]) -> bool:\n",
    "        dists = route[-1]\n",
    "        cycle = min(pos[0] // dists[0], pos[1] // dists[1])\n",
    "        location = [dists[0] * cycle, dists[1] * cycle]\n",
    "        if location[0] == pos[0] and location[1] == pos[1]:\n",
    "            return True\n",
    "        for point in route:\n",
    "            xx = location[0] + point[0]\n",
    "            yy = location[1] + point[1]\n",
    "            if xx == pos[0] and yy == pos[1]:\n",
    "                return True\n",
    "        return False\n",
    "    \n",
    "    def getRoute(self, command: str) -> List[List[int]]:\n",
    "        route = []\n",
    "        x, y = 0, 0\n",
    "        for cmd in command:\n",
    "            if cmd == 'U':\n",
    "                y += 1\n",
    "            elif cmd == 'R':\n",
    "                x += 1\n",
    "            route.append([x, y])\n",
    "        return route\n",
    "        \n",
    "            \n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        if x==0 and y==0:\n",
    "            return True\n",
    "    \n",
    "        path = set([(0,0)])\n",
    "        i, j = 0, 0\n",
    "        for c in command:\n",
    "            if c == 'U':\n",
    "                j += 1\n",
    "            else:\n",
    "                i += 1\n",
    "            path.add((i,j))\n",
    "\n",
    "        def div(x:int, y:int) -> Tuple[int, int]:\n",
    "            m = min(x//i, y//j)\n",
    "            return (x-i*m, y-j*m)\n",
    "        \n",
    "        #print(path)\n",
    "        if div(x,y) not in path:\n",
    "            return False\n",
    "\n",
    "        for a, b in obstacles:\n",
    "            if a>x or b>y:\n",
    "                continue\n",
    "            if div(a, b) in path:\n",
    "                #print(a,b)\n",
    "                return False\n",
    "        return True\n",
    "\n",
    "\n",
    "#RRU\n",
    "#0,0 1,0, 2,0, 2,1\n",
    "#    3,1, 4,1, 4,2\n",
    "#    5,2, 6,2, 6,3\n",
    "\n",
    "#RRUUU\n",
    "#0,0 1,0, 2,0, 2,1, 2,2, 2,3\n",
    "#    3,3, 4,3, 4,4, 4,5, 4,6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        a, b = 0, 0\n",
    "        for s in command:\n",
    "            if s == 'U': b += 1\n",
    "            if s == 'R': a += 1\n",
    "        kk = []\n",
    "        for i, obstacle in enumerate(obstacles):\n",
    "            if obstacle[0] > x or obstacle[1] > y: continue\n",
    "            temp = min(obstacle[0] // a, obstacle[1] // b)\n",
    "            if [obstacle[0] - temp * a, obstacle[1] - temp * b] == [0, 0]:\n",
    "                kk.append([a, b])\n",
    "            else:\n",
    "                kk.append([obstacle[0] - temp * a, obstacle[1] - temp * b])\n",
    "        \n",
    "        min_circle = min(x // a, y // b)\n",
    "        x, y = x - min_circle * a, y - min_circle * b\n",
    "        ans = [0, 0]\n",
    "        flag = 0\n",
    "        for s in command:\n",
    "            if ans == [x, y]:\n",
    "                if min_circle == 0: return True\n",
    "                else: flag = 1\n",
    "            if s == 'U': ans[1] = 1 + ans[1]\n",
    "            elif s == 'R': ans[0] = 1 + ans[0]\n",
    "            if ans in kk: return False\n",
    "        if flag == 1: return True\n",
    "        return False\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "'''\n",
    "判断会在多少步经过某一点\n",
    "'''\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        \n",
    "        n = len(command)\n",
    "        uPre = [0] * (n + 1)\n",
    "        rPre = [0] * (n + 1)\n",
    "\n",
    "        for i in range(n):\n",
    "            uPre[i+1] = uPre[i]\n",
    "            rPre[i+1] = rPre[i]\n",
    "\n",
    "            if command[i] == \"U\":\n",
    "                uPre[i+1] += 1\n",
    "                lastU = i\n",
    "            else:\n",
    "                rPre[i+1] += 1\n",
    "                lastR = i\n",
    "\n",
    "        def isVisit(x, y):\n",
    "            #需要达到x, 需要完整走过rdX轮，还要走resX步\n",
    "            rdX = x // rPre[-1]\n",
    "            if rdX * rPre[-1] == x:\n",
    "                #刚好完整走过\n",
    "                rdX = rdX - 1\n",
    "                resX = lastR + 1   #lastR为在command中最后出现的位置，对应rPre需要+1\n",
    "            else:    \n",
    "                resX = rPre.index(x - rdX * rPre[-1])\n",
    "\n",
    "            #需要达到y, 需要完整走过rdY轮，还要走resY步\n",
    "            udY = y // uPre[-1]\n",
    "            if udY * uPre[-1] == y:\n",
    "                udY -= 1\n",
    "                resY = lastU + 1\n",
    "            else:    \n",
    "                resY = uPre.index(y - udY * uPre[-1])\n",
    "\n",
    "            had = [(rdX, resX), (udY, resY)]\n",
    " \n",
    "            had.sort(key = lambda x : (-x[0], -x[1]))\n",
    "\n",
    "            xx = had[0][0] * rPre[-1] + rPre[had[0][1]]\n",
    "            yy = had[0][0] * uPre[-1] + uPre[had[0][1]]\n",
    "\n",
    "\n",
    "            if (xx, yy) == (x, y):\n",
    "                return had[0]\n",
    "            return False\n",
    "\n",
    "\n",
    "        tar = isVisit(x, y)\n",
    "        #print(tar)\n",
    "        if tar == False:\n",
    "            return False\n",
    "        for x, y in obstacles:\n",
    "            temp = isVisit(x, y)\n",
    "            if temp == False:\n",
    "                continue\n",
    "            if temp <= tar:\n",
    "                return False\n",
    "\n",
    "        return True\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        def if_arrive(target):    \n",
    "            c = [0, 0]\n",
    "            iter_num1 = target[0] // moves[-1][0]\n",
    "            iter_num2 = target[1] // moves[-1][1]\n",
    "            if iter_num1 <= iter_num2:\n",
    "                iter_num = iter_num1\n",
    "                index = 0\n",
    "            else:\n",
    "                iter_num = iter_num2\n",
    "                index = 1\n",
    "            c[index] = target[index] % moves[-1][index]\n",
    "            c[1 - index] = target[1 - index] - (iter_num * moves[-1][1 - index])\n",
    "            c[1 - index] - c[1 - index] if c[1 - index] > 0 else 0\n",
    "            if c in moves and target[0] <= x and target[1] <= y:\n",
    "                return False\n",
    "            return True\n",
    "        xi = 0\n",
    "        yi = 0\n",
    "        moves = [[0, 0]]\n",
    "        for move in command:\n",
    "            if move == 'U':\n",
    "                yi += 1\n",
    "            else:\n",
    "                xi += 1\n",
    "            if [xi, yi] in obstacles:\n",
    "                return False\n",
    "            moves.append([xi, yi])\n",
    "        for obstacle in obstacles:\n",
    "            if not if_arrive(obstacle):\n",
    "                return False\n",
    "        if if_arrive([x, y]):\n",
    "            return False\n",
    "        return True\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        if x==0 and y==0:\n",
    "            return True\n",
    "    \n",
    "        path = []\n",
    "        i, j = 0, 0\n",
    "        for c in command:\n",
    "            if c == 'U':\n",
    "                j += 1\n",
    "            else:\n",
    "                i += 1\n",
    "            path.append((i,j))\n",
    "\n",
    "        def check(a:int, b:int, x:int, y:int) -> bool:\n",
    "            da, db = x-a, y-b\n",
    "            if da < 0 or db < 0:\n",
    "                return False\n",
    "            if (da%i != 0) or (db%j != 0):\n",
    "                return False\n",
    "            return da//i == db//j\n",
    "        \n",
    "        #print(path)\n",
    "        if not any([check(a, b, x, y) for a,b in path]):\n",
    "            return False\n",
    "\n",
    "        for a, b in obstacles:\n",
    "            if a>x or b>y:\n",
    "                continue\n",
    "            if any([check(pa, pb, a, b) for pa, pb in path]):\n",
    "                #print(a,b)\n",
    "                return False\n",
    "        return True\n",
    "\n",
    "\n",
    "#RRU\n",
    "#0,0 1,0, 2,0, 2,1\n",
    "#    3,1, 4,1, 4,2\n",
    "#    5,2, 6,2, 6,3\n",
    "\n",
    "#RRUUU\n",
    "#0,0 1,0, 2,0, 2,1, 2,2, 2,3\n",
    "#    3,3, 4,3, 4,4, 4,5, 4,6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        a=b= 0\n",
    "        # 计算第一次执行完command后, 走过的所有坐标,直接进集合\n",
    "        s = set([(0, 0)])\n",
    "        for c in command:\n",
    "            if c == 'R' :a+= 1\n",
    "            else:b+= 1\n",
    "            s.add((a,b))\n",
    "        c = min(x // a, y // b)\n",
    "        if (x-a * c, y-b* c) not in s:return False\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for ax, ay  in obstacles:\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            c = min(ax // a, ay// b)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ax <= x and ay <= y and (ax - a* c, ay - b * c) in s:\n",
    "                return False \n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        xi = 0\n",
    "        yi = 0\n",
    "\n",
    "        # 计算第一次执行完command后, 走过的所有坐标\n",
    "        first_coor = [[0, 0]]\n",
    "        for c in command:\n",
    "            if c == 'R' :\n",
    "                xi += 1\n",
    "            else:\n",
    "                yi += 1\n",
    "            first_coor.append([xi, yi])\n",
    "        \n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // xi, y // yi)\n",
    "\n",
    "        if [x - xi * circle, y - yi * circle] not in first_coor:\n",
    "            return False\n",
    "\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for obstacle in obstacles:\n",
    "            ob_x, ob_y = obstacle[0], obstacle[1]\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            circle = min(ob_x // xi, ob_y // yi)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ob_x <= x and ob_y <= y and [ob_x - xi * circle, ob_y - yi * circle] in first_coor:\n",
    "                return False \n",
    "\n",
    "        return True\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        xx, yy = 0, 0\n",
    "        dot_list = [[0, 0]]\n",
    "        for i in command:\n",
    "            if i == 'R':\n",
    "                xx += 1\n",
    "            else:\n",
    "                yy += 1\n",
    "            dot_list.append([xx, yy])\n",
    "        xb = dot_list[-1][0]\n",
    "        yb = dot_list[-1][1]\n",
    "        b = min(x // xb, y // yb)\n",
    "        if [x - b * xb, y - b * yb] not in dot_list:\n",
    "            return False\n",
    "        else:\n",
    "            for i in obstacles:\n",
    "                if i[0] > x or i[1] >y:\n",
    "                    continue\n",
    "                b = min(i[0] // xb, i[1] // yb)\n",
    "                if [i[0] - b * xb, i[1] - b * yb] in dot_list:\n",
    "                    return False\n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        xx, yy = 0, 0\n",
    "        dot_list = [[0, 0]]\n",
    "        for i in command:\n",
    "            if i == 'R':\n",
    "                xx += 1\n",
    "            else:\n",
    "                yy += 1\n",
    "            dot_list.append([xx, yy])\n",
    "        xb = dot_list[-1][0]\n",
    "        yb = dot_list[-1][1]\n",
    "        for i in obstacles:\n",
    "            if i[0] > x or i[1] >y:\n",
    "                continue\n",
    "            b = min(i[0] // xb, i[1] // yb)\n",
    "            if [i[0] - b * xb, i[1] - b * yb] in dot_list:\n",
    "                return False\n",
    "        b = min(x // xb, y // yb)\n",
    "        if [x - b * xb, y - b *yb] in dot_list:\n",
    "            return True\n",
    "        else:\n",
    "            return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        xx = yy = 0\n",
    "        trace = {(0, 0)}\n",
    "        for c in command:\n",
    "            xx += 1 if c == 'R' else 0\n",
    "            yy += 1 if c == 'U' else 0\n",
    "            trace.add((xx, yy))\n",
    "        circle = min(x//xx, y//yy)\n",
    "        if (x-circle*xx, y-circle*yy) not in trace:\n",
    "            return False\n",
    "        for o_x, o_y in obstacles:\n",
    "            if o_x > x or o_y > y:\n",
    "                continue\n",
    "            circle = min(o_x//xx, o_y//yy)\n",
    "            if (o_x-circle*xx, o_y-circle*yy) in trace:\n",
    "                return False\n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        rotue = []\n",
    "        rotue.append([0,0])\n",
    "        x_c,y_c = 0,0\n",
    "        for c in command:\n",
    "            if c==\"R\":\n",
    "                x_c += 1\n",
    "                rotue.append([x_c,y_c])\n",
    "            else:\n",
    "                y_c += 1\n",
    "                rotue.append([x_c,y_c])    \n",
    "\n",
    "        def yu(x1,y1):\n",
    "            cntx,cnty = x1//x_c,y1//y_c\n",
    "            #循环次数\n",
    "            cnt = min(cntx,cnty)\n",
    "            #剩余需要判断是否在一个循环路径里面\n",
    "            yu_x,yu_y = x1-cnt*x_c,y1-cnt*y_c\n",
    "            return [yu_x,yu_y]\n",
    "        if yu(x,y) not in rotue:\n",
    "            return False\n",
    "        for obs in obstacles:\n",
    "            ox,oy = obs[0],obs[1]\n",
    "            if ox<=x and oy<=y and yu(ox,oy) in rotue:\n",
    "                return False\n",
    "        return True\n",
    "\n",
    "        \n",
    "\n",
    "        \"\"\"\n",
    "      暴力超时\n",
    "      \"\"\"\n",
    "    #   locx,locy = 0,0\n",
    "    #   while locx<=x and locy<=y:\n",
    "    #     for i in range(len(command)):\n",
    "    #       if command[i]==\"U\":\n",
    "    #         locy += 1\n",
    "    #       if command[i]==\"R\":\n",
    "    #         locx +=1\n",
    "    #       if [locx,locy] in obstacles:\n",
    "    #         return False\n",
    "    #       if locx==x and locy==y:\n",
    "    #         return True\n",
    "    #   return False\n",
    "   \n",
    "        \n",
    "\n",
    "\n",
    "\n",
    "    \n",
    "\n",
    "        \n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        i = j = 0\n",
    "        for step in command:\n",
    "            if step == 'U':\n",
    "                j += 1\n",
    "            else:\n",
    "                i += 1\n",
    "\n",
    "        def arrive(dx,dy):\n",
    "            loop = (dx+dy)//len(command)\n",
    "            res = (dx+dy)%len(command)\n",
    "            step_i = i * loop\n",
    "            step_j = j * loop\n",
    "            if res != 0:\n",
    "                for step in command[0:res]:\n",
    "                    if step == 'U':\n",
    "                        step_j += 1\n",
    "                    else:\n",
    "                        step_i += 1\n",
    "            if step_i == dx and step_j == dy:\n",
    "                return True\n",
    "        if not arrive(x,y):\n",
    "            return False\n",
    "\n",
    "        for dx,dy in obstacles:\n",
    "            if dx + dy >= x + y:\n",
    "                continue\n",
    "            if arrive(dx,dy):\n",
    "                return False\n",
    "            \n",
    "        return True\n",
    "            \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        a=b= 0\n",
    "        # 计算第一次执行完command后, 走过的所有坐标,直接进集合\n",
    "        s = set([(0, 0)])\n",
    "        for c in command:\n",
    "            if c == 'R' :a+= 1\n",
    "            else:b+= 1\n",
    "            s.add((a,b))\n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // a, y // b)\n",
    "        if (x-a * circle, y-b* circle) not in s:return False\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for ax, ay  in obstacles:\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            c = min(ax // a, ay// b)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ax <= x and ay <= y and (ax - a* c, ay - b * c) in s:\n",
    "                return False \n",
    "\n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        xx, yy = 0, 0\n",
    "        dot_list = [[0, 0]]\n",
    "        for i in command:\n",
    "            if i == 'R':\n",
    "                xx += 1\n",
    "            else:\n",
    "                yy += 1\n",
    "            dot_list.append([xx, yy])\n",
    "        xb = dot_list[-1][0]\n",
    "        yb = dot_list[-1][1]\n",
    "        b = min(x // xb, y // yb)\n",
    "        if [x - b * xb, y - b * yb] not in dot_list:\n",
    "            return False\n",
    "        else:\n",
    "            for i in obstacles:\n",
    "                if i[0] > x or i[1] >y:\n",
    "                    continue\n",
    "                b = min(i[0] // xb, i[1] // yb)\n",
    "                if [i[0] - b * xb, i[1] - b * yb] in dot_list:\n",
    "                    return False\n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "'''\n",
    "判断会在多少步经过某一点\n",
    "'''\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        \n",
    "        n = len(command)\n",
    "        uPre = [0] * (n + 1)\n",
    "        rPre = [0] * (n + 1)\n",
    "\n",
    "        for i in range(n):\n",
    "            uPre[i+1] = uPre[i]\n",
    "            rPre[i+1] = rPre[i]\n",
    "\n",
    "            if command[i] == \"U\":\n",
    "                uPre[i+1] += 1\n",
    "                lastU = i\n",
    "            else:\n",
    "                rPre[i+1] += 1\n",
    "                lastR = i\n",
    "\n",
    "        def isVisit(x, y):\n",
    "            #需要达到x, 需要完整走过rdX轮，还要走resX步\n",
    "            rdX = x // rPre[-1]\n",
    "            if rdX * rPre[-1] == x:\n",
    "                #刚好完整走过\n",
    "                rdX = rdX - 1\n",
    "                resX = lastR + 1\n",
    "            else:    \n",
    "                resX = rPre.index(x - rdX * rPre[-1])\n",
    "\n",
    "            #需要达到y, 需要完整走过rdY轮，还要走resY步\n",
    "            udY = y // uPre[-1]\n",
    "            if udY * uPre[-1] == y:\n",
    "                udY -= 1\n",
    "                resY = lastU + 1\n",
    "            else:    \n",
    "                resY = uPre.index(y - udY * uPre[-1])\n",
    "\n",
    "            had = [(rdX, resX), (udY, resY)]\n",
    "            #print(had)\n",
    "            had.sort(key = lambda x : (-x[0], -x[1]))\n",
    "\n",
    "            xx = had[0][0] * rPre[-1] + rPre[had[0][1]]\n",
    "            yy = had[0][0] * uPre[-1] + uPre[had[0][1]]\n",
    "\n",
    "            #print(had)\n",
    "\n",
    "            if (xx, yy) == (x, y):\n",
    "                return had[0]\n",
    "            return False\n",
    "\n",
    "\n",
    "        tar = isVisit(x, y)\n",
    "        #print(tar)\n",
    "        if tar == False:\n",
    "            return False\n",
    "        for x, y in obstacles:\n",
    "            temp = isVisit(x, y)\n",
    "            if temp == False:\n",
    "                continue\n",
    "            if temp <= tar:\n",
    "                return False\n",
    "\n",
    "\n",
    "\n",
    "        return True\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        a, b = 0, 0\n",
    "        locus = [[0,0]]\n",
    "        for i in command:\n",
    "            if i == 'U':\n",
    "                a += 1\n",
    "            else:\n",
    "                b += 1\n",
    "            locus.append([b, a])\n",
    "        # 假设一定能到达终点\n",
    "        circle = min(x//b, y//a)\n",
    "        if [x-b*circle, y-a*circle] not in locus:\n",
    "            return False\n",
    "        for o in obstacles:\n",
    "            i, j = o[0], o[1]\n",
    "            if i>x or j>y:\n",
    "                continue\n",
    "            c = min(i//b, j//a)\n",
    "            if [i-b*c, j-a*c] in locus:\n",
    "                return False\n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        xx, yy = 0, 0\n",
    "        dot_list = [[0, 0]]\n",
    "        for i in command:\n",
    "            if i == 'R':\n",
    "                xx += 1\n",
    "            else:\n",
    "                yy += 1\n",
    "            dot_list.append([xx, yy])\n",
    "        xb = dot_list[-1][0]\n",
    "        yb = dot_list[-1][1]\n",
    "        for i in obstacles:\n",
    "            if i[0] > x or i[1] >y:\n",
    "                continue\n",
    "            b = min(i[0] // xb, i[1] // yb)\n",
    "            if [i[0] - b * xb, i[1] - b * yb] in dot_list:\n",
    "                return False\n",
    "        b = min(x // xb, y // yb)\n",
    "        if [x - b * xb, y - b *yb] in dot_list:\n",
    "            return True\n",
    "        else:\n",
    "            return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        xi=0\n",
    "        yi=0\n",
    "        first_coor = [[0,0]]\n",
    "        for c in command:\n",
    "            if c == 'R':\n",
    "                xi+=1\n",
    "            else:\n",
    "                yi+=1\n",
    "            first_coor.append([xi,yi])\n",
    "        circle = min(x//xi,y//yi)\n",
    "        if [x-xi*circle,y-yi*circle] not in first_coor:\n",
    "            return False\n",
    "        for ob in obstacles:\n",
    "            ob_x,ob_y = ob[0],ob[1]\n",
    "            circle =  min(ob_x//xi,ob_y//yi)\n",
    "            if ob_x<=x and ob_y<=y and [ob_x - xi*circle,ob_y-yi*circle] in first_coor:\n",
    "                return False\n",
    "        return True\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "\n",
    "        xi = 0\n",
    "        yi = 0\n",
    "\n",
    "        # 计算第一次执行完command后, 走过的所有坐标\n",
    "        first_coor = [[0, 0]]\n",
    "        for c in command:\n",
    "            if c == 'R' :\n",
    "                xi += 1\n",
    "            else:\n",
    "                yi += 1\n",
    "            first_coor.append([xi, yi])\n",
    "        \n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // xi, y // yi)\n",
    "\n",
    "        if [x - xi * circle, y - yi * circle] not in first_coor:\n",
    "            return False\n",
    "\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for obstacle in obstacles:\n",
    "            ob_x, ob_y = obstacle[0], obstacle[1]\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            circle = min(ob_x // xi, ob_y // yi)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ob_x <= x and ob_y <= y and [ob_x - xi * circle, ob_y - yi * circle] in first_coor:\n",
    "                return False \n",
    "\n",
    "        return True\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], A: int, B: int) -> bool:\n",
    "        x = y = 0\n",
    "        tmp = []\n",
    "        for i,j in obstacles:\n",
    "            tmp.append(i*10**10 + j)\n",
    "        s = set(tmp)\n",
    "\n",
    "        while True:\n",
    "            for i in command:\n",
    "                if i == 'U':\n",
    "                    y+=1\n",
    "                else:\n",
    "                    x+=1\n",
    "\n",
    "                if x*10**10 + y in s:\n",
    "                    return False\n",
    "                if x > A and y > B:\n",
    "                    return False\n",
    "                if x == A and y == B:\n",
    "                    return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        n=x+y\n",
    "        m=len(command)\n",
    "        a=[0]*(m+1)\n",
    "        for i in range(m):\n",
    "            if command[i] == 'R':\n",
    "                a[i+1]=a[i]+1\n",
    "            else:\n",
    "                a[i+1]=a[i]\n",
    "        \n",
    "        for ob in obstacles:\n",
    "            t = ob[0]+ob[1]\n",
    "            if t < n:\n",
    "                t1 = t % m\n",
    "                t2 = t // m\n",
    "                ob[0] -= t2*a[-1]\n",
    "                if a[t1] == ob[0]:\n",
    "                    return False\n",
    "        \n",
    "        t1 = n % m\n",
    "        t2 = n // m\n",
    "        x -= t2*a[-1]\n",
    "        if a[t1] == x:\n",
    "            return True\n",
    "        return False\n",
    "        \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "import time\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "\n",
    "        xi = 0\n",
    "        yi = 0\n",
    "\n",
    "        # 计算第一次执行完command后, 走过的所有坐标\n",
    "        first_coor = [[0, 0]]\n",
    "        for c in command:\n",
    "            if c == 'R' :\n",
    "                xi += 1\n",
    "            else:\n",
    "                yi += 1\n",
    "            first_coor.append([xi, yi])\n",
    "        \n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // xi, y // yi)\n",
    "\n",
    "        if [x - xi * circle, y - yi * circle] not in first_coor:\n",
    "            return False\n",
    "\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for obstacle in obstacles:\n",
    "            ob_x, ob_y = obstacle[0], obstacle[1]\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            circle = min(ob_x // xi, ob_y // yi)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ob_x <= x and ob_y <= y and [ob_x - xi * circle, ob_y - yi * circle] in first_coor:\n",
    "                return False \n",
    "        time.sleep(0.5)\n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        xx, yy = 0, 0\n",
    "        dot_list = [[0, 0]]\n",
    "        for i in command:\n",
    "            if i == 'R':\n",
    "                xx += 1\n",
    "            else:\n",
    "                yy += 1\n",
    "            dot_list.append([xx, yy])\n",
    "        xb = dot_list[-1][0]\n",
    "        yb = dot_list[-1][1]\n",
    "        for i in obstacles:\n",
    "            if i[0] > x or i[1] >y:\n",
    "                continue\n",
    "            b = min(i[0] // xb, i[1] // yb)\n",
    "            if [i[0] - b * xb, i[1] - b * yb] in dot_list:\n",
    "                return False\n",
    "        b = min(x // xb, y // yb)\n",
    "        if [x - b * xb, y - b *yb] in dot_list:\n",
    "            return True\n",
    "        else:\n",
    "            return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        xi =yi= 0\n",
    "        # 计算第一次执行完command后, 走过的所有坐标\n",
    "        first_coor = [(0, 0)]\n",
    "        for c in command:\n",
    "            if c == 'R' :xi += 1\n",
    "            else:yi += 1\n",
    "            first_coor.append((xi, yi))\n",
    "        first_coor=set(first_coor)\n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // xi, y // yi)\n",
    "        if (x-xi * circle, y-yi * circle) not in first_coor:return False\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for ax, ay  in obstacles:\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            circle = min(ax // xi, ay// yi)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ax <= x and ay <= y and (ax - xi * circle, ay - yi * circle) in first_coor:\n",
    "                return False \n",
    "\n",
    "        return True\n",
    "'''\n",
    "作者：dongmen\n",
    "链接：https://leetcode.cn/problems/programmable-robot/solutions/55299/python-chang-gui-jie-fa-jian-dan-yi-dong-by-dong-m/\n",
    "来源：力扣（LeetCode）\n",
    "著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "\n",
    "        xi = 0\n",
    "        yi = 0\n",
    "\n",
    "        # 计算第一次执行完command后, 走过的所有坐标\n",
    "        first_coor = [[0, 0]]\n",
    "        for c in command:\n",
    "            if c == 'R' :\n",
    "                xi += 1\n",
    "            else:\n",
    "                yi += 1\n",
    "            first_coor.append([xi, yi])\n",
    "        \n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // xi, y // yi)\n",
    "\n",
    "        if [x - xi * circle, y - yi * circle] not in first_coor:\n",
    "            return False\n",
    "\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for obstacle in obstacles:\n",
    "            ob_x, ob_y = obstacle[0], obstacle[1]\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            circle = min(ob_x // xi, ob_y // yi)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ob_x <= x and ob_y <= y and [ob_x - xi * circle, ob_y - yi * circle] in first_coor:\n",
    "                return False \n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "\n",
    "        xi = 0\n",
    "        yi = 0\n",
    "\n",
    "        # 计算第一次执行完command后, 走过的所有坐标\n",
    "        first_coor = [(0, 0)]\n",
    "        for c in command:\n",
    "            if c == 'R' :\n",
    "                xi += 1\n",
    "            else:\n",
    "                yi += 1\n",
    "            first_coor.append((xi, yi))\n",
    "        first_coor=set(first_coor)\n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // xi, y // yi)\n",
    "\n",
    "        if (x-xi * circle, y-yi * circle) not in first_coor:return False\n",
    "\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for ob_x, ob_y  in obstacles:\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            circle = min(ob_x // xi, ob_y // yi)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ob_x <= x and ob_y <= y and (ob_x - xi * circle, ob_y - yi * circle) in first_coor:\n",
    "                return False \n",
    "\n",
    "        return True\n",
    "'''\n",
    "作者：dongmen\n",
    "链接：https://leetcode.cn/problems/programmable-robot/solutions/55299/python-chang-gui-jie-fa-jian-dan-yi-dong-by-dong-m/\n",
    "来源：力扣（LeetCode）\n",
    "著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "import time\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "\n",
    "        xi = 0\n",
    "        yi = 0\n",
    "\n",
    "        # 计算第一次执行完command后, 走过的所有坐标\n",
    "        first_coor = [[0, 0]]\n",
    "        for c in command:\n",
    "            if c == 'R' :\n",
    "                xi += 1\n",
    "            else:\n",
    "                yi += 1\n",
    "            first_coor.append([xi, yi])\n",
    "        \n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // xi, y // yi)\n",
    "\n",
    "        if [x - xi * circle, y - yi * circle] not in first_coor:\n",
    "            return False\n",
    "\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for obstacle in obstacles:\n",
    "            ob_x, ob_y = obstacle[0], obstacle[1]\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            circle = min(ob_x // xi, ob_y // yi)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ob_x <= x and ob_y <= y and [ob_x - xi * circle, ob_y - yi * circle] in first_coor:\n",
    "                return False \n",
    "        time.sleep(0.2)\n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "\n",
    "        xi = 0\n",
    "        yi = 0\n",
    "\n",
    "        # 计算第一次执行完command后, 走过的所有坐标\n",
    "        first_coor = [[0, 0]]\n",
    "        for c in command:\n",
    "            if c == 'R' :\n",
    "                xi += 1\n",
    "            else:\n",
    "                yi += 1\n",
    "            first_coor.append([xi, yi])\n",
    "        \n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // xi, y // yi)\n",
    "\n",
    "        if [x - xi * circle, y - yi * circle] not in first_coor:\n",
    "            return False\n",
    "\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for obstacle in obstacles:\n",
    "            ob_x, ob_y = obstacle[0], obstacle[1]\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            circle = min(ob_x // xi, ob_y // yi)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ob_x <= x and ob_y <= y and [ob_x - xi * circle, ob_y - yi * circle] in first_coor:\n",
    "                return False \n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        move = [0,0]\n",
    "\n",
    "        for i in command:\n",
    "            if i == \"U\":\n",
    "                move[1] += 1\n",
    "            else:\n",
    "                move[0] += 1\n",
    "        \n",
    "        \n",
    "        zhangai = []\n",
    "        for i in range(len(obstacles)):\n",
    "            if obstacles[i][0] >x or obstacles[i][1] >y:\n",
    "                continue\n",
    "            chu = min(obstacles[i][0]//move[0],obstacles[i][1]//move[1])\n",
    "            zhangai.append([obstacles[i][0]-chu*move[0],\n",
    "                            obstacles[i][1]-chu*move[1]])\n",
    "        chu = min(x//move[0],y//move[1])\n",
    "        x =x-chu*move[0]\n",
    "        y = y-chu*move[1]\n",
    "        \n",
    "        move = [0,0]\n",
    "        flag = False\n",
    "        if move == [x,y]:\n",
    "            flag = True\n",
    "        if move in zhangai:\n",
    "            return False \n",
    "        \n",
    "        for i in command:\n",
    "            if i == \"U\":\n",
    "                move[1] += 1\n",
    "            else:\n",
    "                move[0] += 1\n",
    "            if move == [x,y]:\n",
    "                flag = True\n",
    "            if move in zhangai:\n",
    "                return False \n",
    "\n",
    "        return flag\n",
    "\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        sp = [(0,0)] # 索引每一步的位置\n",
    "        s = len(command) # 一个循环的步数\n",
    "        u=r=0\n",
    "        for c in command:\n",
    "            if c=='U':\n",
    "                u+=1\n",
    "            else:\n",
    "                r+=1\n",
    "            sp.append((r,u))\n",
    "        quo,rem = divmod(x+y,s)\n",
    "        # 终点是否在路径\n",
    "        if quo*r+sp[rem][0]!=x or quo*u+sp[rem][1]!=y:\n",
    "            return False\n",
    "        for i,j in obstacles:\n",
    "            if i+j>x+y: # 比终点远跳过\n",
    "                continue\n",
    "            # 障碍物是否在路径\n",
    "            quo,rem = divmod(i+j,s)\n",
    "            if quo*r+sp[rem][0]==i and quo*u+sp[rem][1]==j:\n",
    "                return False\n",
    "        return True\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        def judge(command,xx,yy,x,y):\n",
    "            k=0\n",
    "            if xx!=0 and yy!=0:\n",
    "                k=min(x//xx,y//yy)\n",
    "            elif xx==0:\n",
    "                k=y//yy\n",
    "            elif yy==0:\n",
    "                k=x//xx\n",
    "            x-=k*xx\n",
    "            y-=k*yy\n",
    "            tempx,tempy=0,0\n",
    "            for i in range(len(command)):\n",
    "                if tempx==x and tempy==y:\n",
    "                    return True\n",
    "                if command[i]=='U':\n",
    "                    tempy+=1\n",
    "                if command[i]=='R':\n",
    "                    tempx+=1\n",
    "            return False               \n",
    "        n=len(command)\n",
    "        xx,yy=0,0\n",
    "        for i in range(n):\n",
    "            if [xx,yy] in obstacles:\n",
    "                return False\n",
    "            if xx==x and yy==y:\n",
    "                return True\n",
    "            if command[i]=='U':\n",
    "                yy+=1\n",
    "            if command[i]=='R':\n",
    "                xx+=1\n",
    "        if judge(command,xx,yy,x,y)==False:\n",
    "            return False\n",
    "        if len(obstacles)>0:\n",
    "            for j in range(len(obstacles)):\n",
    "                xxx=obstacles[j][0]\n",
    "                yyy=obstacles[j][1]\n",
    "                if xxx<=x and yyy<=y and judge(command,xx,yy,xxx,yyy)==True:\n",
    "                    return False\n",
    "            return True\n",
    "        if len(obstacles)==0:\n",
    "            return True\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        xi =yi= 0\n",
    "        # 计算第一次执行完command后, 走过的所有坐标\n",
    "        first_coor = set([(0, 0)])\n",
    "        for c in command:\n",
    "            if c == 'R' :xi += 1\n",
    "            else:yi += 1\n",
    "            first_coor.add((xi, yi))\n",
    "        \n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // xi, y // yi)\n",
    "        if (x-xi * circle, y-yi * circle) not in first_coor:return False\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for ax, ay  in obstacles:\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            circle = min(ax // xi, ay// yi)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ax <= x and ay <= y and (ax - xi * circle, ay - yi * circle) in first_coor:\n",
    "                return False \n",
    "\n",
    "        return True\n",
    "'''\n",
    "作者：dongmen\n",
    "链接：https://leetcode.cn/problems/programmable-robot/solutions/55299/python-chang-gui-jie-fa-jian-dan-yi-dong-by-dong-m/\n",
    "来源：力扣（LeetCode）\n",
    "著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        right,upper = 0,0\n",
    "        one_circle = []\n",
    "        one_circle.append([0,0])\n",
    "        for i in range(len(command)):\n",
    "            if command[i] == 'U':\n",
    "                upper += 1\n",
    "            else:\n",
    "                right += 1\n",
    "            one_circle.append([right,upper])\n",
    "        \n",
    "        circle = min(x//right,y//upper)\n",
    "        x_origin = x - right * circle\n",
    "        y_origin = y - upper * circle\n",
    "        if [x_origin,y_origin] not in one_circle:\n",
    "            return False    \n",
    "        for obstacle in obstacles:\n",
    "            if obstacle[0] <= x and obstacle[1] <= y:\n",
    "                obstacle_circle = min(obstacle[0] // right,obstacle[1] // upper)  # 任意一个障碍物最小的循环次数\n",
    "                if [obstacle[0]-right * obstacle_circle,obstacle[1] - upper * obstacle_circle] in one_circle: # 任意一个障碍物的起始点\n",
    "                    return False\n",
    "        \n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "\n",
    "        xi = 0\n",
    "        yi = 0\n",
    "\n",
    "        # 计算第一次执行完command后, 走过的所有坐标\n",
    "        first_coor = [[0, 0]]\n",
    "        for c in command:\n",
    "            if c == 'R' :\n",
    "                xi += 1\n",
    "            else:\n",
    "                yi += 1\n",
    "            first_coor.append([xi, yi])\n",
    "        \n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // xi, y // yi)\n",
    "\n",
    "        if [x - xi * circle, y - yi * circle] not in first_coor:\n",
    "            return False\n",
    "\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for obstacle in obstacles:\n",
    "            ob_x, ob_y = obstacle[0], obstacle[1]\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            circle = min(ob_x // xi, ob_y // yi)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ob_x <= x and ob_y <= y and [ob_x - xi * circle, ob_y - yi * circle] in first_coor:\n",
    "                return False \n",
    "\n",
    "        return True\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "\n",
    "\n",
    "        xi = 0\n",
    "        yi = 0\n",
    "\n",
    "        # 计算第一次执行完command后, 走过的所有坐标\n",
    "        first_coor = [[0, 0]]\n",
    "        for c in command:\n",
    "            if c == 'R' :\n",
    "                xi += 1\n",
    "            else:\n",
    "                yi += 1\n",
    "            first_coor.append([xi, yi])\n",
    "        \n",
    "        # 此时(xi, yi)也代表着初次command执行结束时走到的最后一个坐标\n",
    "        # 走到目标点所需要的循环次数\n",
    "        circle = min(x // xi, y // yi)\n",
    "\n",
    "        if [x - xi * circle, y - yi * circle] not in first_coor:\n",
    "            return False\n",
    "\n",
    "        # 对每个阻碍点逐个判断，是否会走到它\n",
    "        for obstacle in obstacles:\n",
    "            ob_x, ob_y = obstacle[0], obstacle[1]\n",
    "            # 走到阻碍点所需要的循环数（若是能走到的话）\n",
    "            circle = min(ob_x // xi, ob_y // yi)\n",
    "            # 判断是否会走到这个阻碍点，注意在终点之后的阻碍点可以忽略\n",
    "            if ob_x <= x and ob_y <= y and [ob_x - xi * circle, ob_y - yi * circle] in first_coor:\n",
    "                return False \n",
    "\n",
    "        return True\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "'''\n",
    "判断会在多少步经过某一点\n",
    "'''\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        \n",
    "        n = len(command)\n",
    "        uPre = [0] * (n + 1)\n",
    "        rPre = [0] * (n + 1)\n",
    "\n",
    "        for i in range(n):\n",
    "            uPre[i+1] = uPre[i]\n",
    "            rPre[i+1] = rPre[i]\n",
    "\n",
    "            if command[i] == \"U\":\n",
    "                uPre[i+1] += 1\n",
    "                lastU = i\n",
    "            else:\n",
    "                rPre[i+1] += 1\n",
    "                lastR = i\n",
    "\n",
    "        def isVisit(x, y):\n",
    "            #需要达到x, 需要完整走过rdX轮，还要走resX步\n",
    "            rdX = x // rPre[-1]\n",
    "            if rdX * rPre[-1] == x:\n",
    "                #刚好完整走过\n",
    "                rdX = rdX - 1\n",
    "                resX = lastR + 1   #lastR为在command中最后出现的位置，对应rPre需要+1\n",
    "            else:    \n",
    "                resX = rPre.index(x - rdX * rPre[-1])\n",
    "\n",
    "            #需要达到y, 需要完整走过rdY轮，还要走resY步\n",
    "            udY = y // uPre[-1]\n",
    "            if udY * uPre[-1] == y:\n",
    "                udY -= 1\n",
    "                resY = lastU + 1\n",
    "            else:    \n",
    "                resY = uPre.index(y - udY * uPre[-1])\n",
    "\n",
    "            had = [(rdX, resX), (udY, resY)]\n",
    " \n",
    "            had.sort(key = lambda x : (-x[0], -x[1]))\n",
    "\n",
    "            xx = had[0][0] * rPre[-1] + rPre[had[0][1]]\n",
    "            yy = had[0][0] * uPre[-1] + uPre[had[0][1]]\n",
    "\n",
    "\n",
    "            if (xx, yy) == (x, y):\n",
    "                return had[0]\n",
    "            return False\n",
    "\n",
    "\n",
    "        tar = isVisit(x, y)\n",
    "\n",
    "        if tar == False:\n",
    "            return False\n",
    "        for x, y in obstacles:\n",
    "            temp = isVisit(x, y)\n",
    "            if temp == False:\n",
    "                continue\n",
    "            if temp <= tar:\n",
    "                return False\n",
    "\n",
    "        return True\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        steps = [(0, 0)]\n",
    "        i = 0\n",
    "        j = 0\n",
    "        us = 0\n",
    "        rs = 0\n",
    "        for c in command:\n",
    "            if c == 'U':\n",
    "                j += 1\n",
    "                us += 1\n",
    "            else:\n",
    "                i += 1\n",
    "                rs += 1\n",
    "            steps.append((i, j))\n",
    "\n",
    "        def catch(x, y):\n",
    "            nonlocal us, rs, steps\n",
    "            n = min(x//rs, y//us)\n",
    "            x -= rs*n\n",
    "            y -= us*n\n",
    "            for i, j in steps:\n",
    "                if i == x and j == y:\n",
    "                    return True\n",
    "            return False\n",
    "        if not catch(x, y):\n",
    "            return False\n",
    "\n",
    "        obstacles.sort()\n",
    "        for ox, oy in obstacles:\n",
    "            if ox > x or oy > y:\n",
    "                break\n",
    "            if catch(ox, oy):\n",
    "                return False\n",
    "        return True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def helper(self, command, x_1, y_1, x, y):\n",
    "        # （x_1, y_1）是执行一遍指令第一次歇脚的位置\n",
    "\n",
    "        x_0, y_0, k = 0, 0, 0\n",
    "\n",
    "        # k 计算执行多少轮命令后即将到达终点（下一轮执行肯定能到）\n",
    "        # 使用条件判断计算 k 值，避免除以 0 的情况\n",
    "        if x_1 != 0 and y_1 != 0:\n",
    "            k = min(x // x_1, y // y_1)\n",
    "        elif x_1 == 0:\n",
    "            k = y // y_1\n",
    "        elif y_1 == 0:\n",
    "            k = x // x_1\n",
    "        else:\n",
    "            return True\n",
    "\n",
    "            # 相当于把终点坐标拿到原点附近\n",
    "        x = x - k * x_1\n",
    "        y = y - k * y_1\n",
    "\n",
    "        # 判断下一轮是否会碰到终点\n",
    "        for i in range(len(command)):\n",
    "            if x_0 == x and y_0 == y:\n",
    "                return True\n",
    "\n",
    "            if command[i] == 'U':\n",
    "                y_0 += 1\n",
    "            else:\n",
    "                x_0 += 1\n",
    "\n",
    "        return False\n",
    "\n",
    "    def robot(self, command: str, obstacles: [[int]], x: int, y: int) -> bool:\n",
    "        n = len(command)\n",
    "        x_1 = 0\n",
    "        y_1 = 0\n",
    "        for i in range(n):\n",
    "            if command[i] == 'U':\n",
    "                y_1 += 1\n",
    "            else:\n",
    "                x_1 += 1\n",
    "        if self.helper(command, x_1, y_1, x, y)==False:\n",
    "            return False\n",
    "        for i in range(len(obstacles)):\n",
    "            x_obs = obstacles[i][0]\n",
    "            y_obs = obstacles[i][1]\n",
    "            if x_obs <= x and y_obs <= y and self.helper(command, x_1, y_1, x_obs, y_obs):\n",
    "                return False\n",
    "        return True\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        if x== 0 and y==0:\n",
    "            return True\n",
    "        r_x = 0\n",
    "        r_y = 0\n",
    "        obstacles = set( [ tuple(i) for i in obstacles ] )\n",
    "        while True:\n",
    "            if r_x > x or r_y > y:\n",
    "                return False\n",
    "            for tmp_cmd in command:\n",
    "                if tmp_cmd == 'U':\n",
    "                    r_y += 1\n",
    "                else:\n",
    "                    r_x += 1\n",
    "                if r_x== x and r_y==y:\n",
    "                    return True\n",
    "                else:\n",
    "                    if (r_x,r_y) in obstacles:\n",
    "                        return False\n",
    "                    else:\n",
    "                        continue\n",
    "            \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def robot(self, command: str, obstacles: List[List[int]], x: int, y: int) -> bool:\n",
    "        if x==0 and y==0:return True\n",
    "        \n",
    "        ori,oripos=[0,0],[]\n",
    "        for c in command:\n",
    "            if c=='U':\n",
    "                ori[1]+=1\n",
    "            else:\n",
    "                ori[0]+=1\n",
    "            oripos.append(ori[:])\n",
    "        # print(oripos[-10:])\n",
    "        divpo=oripos[-1]\n",
    "        for po in oripos: #首先检测终点坐标是否在轨迹上\n",
    "            if (x==po[0] and y==po[1]) or (y!=po[1] and (x-po[0])/(y-po[1])==divpo[0]/divpo[1]): #说明坐标(x,y)在行走轨迹上\n",
    "                break\n",
    "        else:return False #顺利遍历完毕，说明终点坐标不在轨迹上，返回false\n",
    "        \n",
    "        for ob in obstacles:\n",
    "            if ob[0]<=x and ob[1]<=y:#先保证障碍物位置在行走路径范围内，否则机器人在走到障碍物之前就先到了终点\n",
    "                for po in oripos:\n",
    "                    if (ob[0]==po[0] and ob[1]==po[1]) or (ob[1]!=po[1] and (ob[0]-po[0])/(ob[1]-po[1])==divpo[0]/divpo[1]):#说明此障碍物在行走轨迹上，返回true\n",
    "                        return False\n",
    "        else:return True #顺利遍历完毕，说明到达终点前没有障碍物在轨迹上，返回true\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    # # 动态规划+堆（优先队列）\n",
    "    # https://leetcode.cn/problems/put-boxes-into-the-warehouse-i/solutions/2392550/ba-xiang-zi-fang-jin-cang-ku-li-i-by-lee-kpx5/\n",
    "    def maxBoxesInWarehouse(self, boxes: List[int], warehouse: List[int]) -> int:\n",
    "        n = len(warehouse)\n",
    "        for i in range(1, n):\n",
    "            warehouse[i] = warehouse[i-1] if warehouse[i-1] < warehouse[i] else warehouse[i]\n",
    "\n",
    "        heapq.heapify(boxes)\n",
    "        ans = 0\n",
    "        for i in range(n-1, -1, -1):\n",
    "            if boxes and boxes[0] <= warehouse[i]:\n",
    "                ans += 1\n",
    "                heapq.heappop(boxes)\n",
    "\n",
    "        return ans\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def putMarbles(self, wt: List[int], k: int) -> int:\n",
    "        for i in range(len(wt) - 1):\n",
    "            wt[i] += wt[i + 1]\n",
    "        wt.pop()\n",
    "        wt.sort()\n",
    "        return sum(wt[len(wt) - k + 1:]) - sum(wt[:k - 1])\n",
    "\n",
    "# 作者：endlesscheng\n",
    "# 链接：https://leetcode.cn/problems/put-marbles-in-bags/solution/wen-ti-zhuan-hua-pai-xu-tan-xin-by-endle-bx8t/\n",
    "# 来源：力扣（LeetCode）\n",
    "# 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        def go(start, end) -> int:\n",
    "            dp = [0] * 2\n",
    "            for i in range(start, end + 1):\n",
    "                dp[1], dp[0] = max(dp[0] + nums[i], dp[1]), dp[1]\n",
    "            return dp[1]\n",
    "        return max(go(0, n - 2), go(1, n - 1)) if n != 1 else nums[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if(len(nums)<=3):\n",
    "            return max(nums)\n",
    "        pre=0\n",
    "        cur=0\n",
    "        for i in nums[:-1]:\n",
    "            pre,cur = cur,max(cur,pre+i)\n",
    "        cur1 = cur\n",
    "        pre=cur=0\n",
    "        for i in nums[1:]:\n",
    "            pre,cur = cur,max(cur,pre+i)\n",
    "        return max(cur,cur1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "# class Solution:\n",
    "#     def rob(self, nums: List[int]) -> int:\n",
    "#         n=len(nums)\n",
    "#         @cache\n",
    "#         def steal(i,limit):\n",
    "#             if i<limit:\n",
    "#                 return 0\n",
    "#             return max(steal(i-1,limit),steal(i-2,limit)+nums[i])\n",
    "#         return max(steal(n-3,1)+nums[n-1],steal(n-2,0))\n",
    "\n",
    "class Solution:\n",
    "    def rob1(self, nums: List[int]) -> int:\n",
    "        f0 = f1 = 0\n",
    "        for i, x in enumerate(nums):\n",
    "            f0, f1 = f1, max(f1, f0 + x)\n",
    "        return f1\n",
    "\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        return max(nums[0] + self.rob1(nums[2:-1]), self.rob1(nums[1:]))\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: [int]) -> int:\n",
    "        def my_rob(nums):\n",
    "            N = len(nums)\n",
    "            dp = [0] * (N+1)\n",
    "            dp[0] = 0\n",
    "            dp[1] = nums[0]\n",
    "            for k in range(2, N+1):\n",
    "                dp[k] = max(dp[k-1], nums[k-1] + dp[k-2])\n",
    "            return dp[N]\n",
    "        return max(my_rob(nums[:-1]),my_rob(nums[1:])) if len(nums) != 1 else nums[0]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        def fun(a):\n",
    "            l, r = 0, 0\n",
    "            for i in a:\n",
    "                l, r = r, max(l+i, r)\n",
    "            return max(l, r)\n",
    "\n",
    "        return max(nums[0]+fun(nums[2:-1]), fun(nums[1:]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "# https://mp.weixin.qq.com/s/z44hk0MW14_mAQd7988mfw\n",
    "# 环形数组，只有2种情况：抢首不抢尾，抢尾不抢首\n",
    "\n",
    "# 节约空间法。要倒序，这里是自底向上\n",
    "# 解释：抢劫，自顶向下，就是从第一家往最后一家走。那么自底向上，那肯定倒着来啊，就是从最后一家往第一家走\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        # base case或者说边界条件\n",
    "        if n == 0:\n",
    "            return 0\n",
    "        \n",
    "        if n == 1:\n",
    "            return nums[0]\n",
    "\n",
    "        return max(self.robby(nums, 0, n-2), self.robby(nums, 1, n-1))\n",
    "\n",
    "    def robby(self, nums, start, end):\n",
    "        dp_i_1, dp_i_2 = 0, 0\n",
    "\n",
    "        dp_i = 0\n",
    "        # 环形数组。这里是倒序。因为这个方法\n",
    "        for i in range(end, start-1, -1):\n",
    "            dp_i = max(dp_i_1, nums[i] + dp_i_2)\n",
    "            # 这种写法是同时交换。不会把新的dp_i_1赋值给dp_i_2的。只会把就的dp_i_1赋值给dp_i_2\n",
    "            dp_i_1, dp_i_2 = dp_i, dp_i_1\n",
    "\n",
    "        return dp_i"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        def not_circle_max_kill(mouse):\n",
    "            if len(mouse) == 0:\n",
    "                return 0\n",
    "            elif len(mouse) == 1:\n",
    "                return mouse[0]\n",
    "\n",
    "            dp = [0] * len(mouse)\n",
    "            dp[0] = mouse[0]\n",
    "            dp[1] = max(mouse[0], mouse[1])\n",
    "            for i in range(2, len(mouse)):\n",
    "                dp[i] = max(dp[i - 2] + mouse[i], dp[i - 1])\n",
    "            return dp[-1]\n",
    "\n",
    "        def circle_max_kill(mouse):\n",
    "            if len(mouse) == 0:\n",
    "                return 0\n",
    "            elif len(mouse) == 1:\n",
    "                return mouse[0]\n",
    "\n",
    "            return max(not_circle_max_kill(mouse[1:]), not_circle_max_kill(mouse[:-1]))\n",
    "\n",
    "        return circle_max_kill(nums)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: [int]) -> int:\n",
    "        def my_rob(nums):\n",
    "            cur, pre = 0, 0\n",
    "            for num in nums:\n",
    "                cur, pre = max(pre + num, cur), cur\n",
    "            return cur\n",
    "        return max(my_rob(nums[:-1]),my_rob(nums[1:])) if len(nums) != 1 else nums[0]\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        def mydp(arr):\n",
    "            if len(arr) == 1:\n",
    "                return arr[0]\n",
    "            elif len(arr) == 2:\n",
    "                return max(arr[0], arr[-1])\n",
    "            elif len(arr) == 0:\n",
    "                return 0\n",
    "\n",
    "            dp = [0] * len(arr)\n",
    "            dp[0], dp[1] = arr[0], max(arr[0], arr[1])\n",
    "\n",
    "            for i in range(2, len(arr)):\n",
    "                dp[i] = max(dp[i-1], dp[i-2] + arr[i])\n",
    "\n",
    "            return dp[-1]\n",
    "\n",
    "        if len(nums)==1:\n",
    "            return nums[0]\n",
    "\n",
    "        return max(mydp(nums[:-1]), mydp(nums[1:]))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if not nums:return 0\n",
    "        if len(nums)==1:return nums[0]\n",
    "        if len(nums)==2:\n",
    "            return max(nums[0],nums[1])\n",
    "        nums1 = nums[:-1]\n",
    "        n = len(nums1)\n",
    "        dp = [0]*n\n",
    "        dp[0] = nums1[0]\n",
    "        dp[1] = max(nums1[0],nums1[1])\n",
    "        for i in range(2, n):\n",
    "            dp[i] = max(dp[i-2]+nums1[i], dp[i-1])\n",
    "        p1 = dp[-1]\n",
    "\n",
    "        nums2 = nums[1:]\n",
    "        n = len(nums2)\n",
    "        dp = [0]*n\n",
    "        dp[0] = nums2[0]\n",
    "        dp[1] = max(nums2[0],nums2[1])\n",
    "        for i in range(2, n):\n",
    "            dp[i] = max(dp[i-2]+nums2[i], dp[i-1])\n",
    "        p2 = dp[-1]\n",
    "        return max(p1, p2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        dp = [0] * len(nums)\n",
    "        if len(nums) == 1:\n",
    "            return nums[0]\n",
    "        dp[1] = nums[0]\n",
    "        for i in range(2,len(nums)):\n",
    "            dp[i] = max(dp[i - 1], dp[i-2] + nums[i - 1])\n",
    "        a = dp[-1]\n",
    "\n",
    "        dp = [0] * len(nums)\n",
    "        dp[1] = nums[1]\n",
    "        for i in range(2,len(nums)):\n",
    "            dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])\n",
    "        return max(a,dp[-1])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if len(nums) <= 2:\n",
    "            return max(nums)\n",
    "\n",
    "        rob1, rob2 = nums[0], 0\n",
    "        for i in range(1, len(nums)-1):\n",
    "            tmp = rob1\n",
    "            rob1 = nums[i] + rob2\n",
    "            rob2 = max(tmp, rob2)\n",
    "        rob1 = max(rob1, rob2)\n",
    "\n",
    "        rob3, rob4 = nums[1], 0\n",
    "        for i in range(2, len(nums)):\n",
    "            tmp = rob3\n",
    "            rob3 = nums[i] + rob4\n",
    "            rob4 = max(tmp, rob4)\n",
    "        rob3 = max(rob3, rob4)\n",
    "        return max(rob1, rob3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def f(self, nums):\n",
    "        n = len(nums)\n",
    "        dp = [0] * n\n",
    "        dp[0] = nums[0]\n",
    "        dp[1] = max(nums[0], nums[1])\n",
    "        for i in range(2, n):\n",
    "            dp[i] = max(dp[i-1], dp[i-2]+nums[i])\n",
    "        return dp[-1]\n",
    "\n",
    "\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if len(nums) == 1:\n",
    "            return nums[0]\n",
    "        if len(nums) == 2:\n",
    "            return max(nums)\n",
    "        return max(self.f(nums[1:]), self.f(nums[:-1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        def rob1(x):\n",
    "            n=len(x)\n",
    "            a,b=x[0],0\n",
    "            if n>1:\n",
    "                for i in range (1,n):\n",
    "                    a,b=b+x[i],max(a,b)\n",
    "            return max(a,b)\n",
    "        \n",
    "        m=len(nums)\n",
    "        if m<=2:\n",
    "            return max(nums)\n",
    "        return max(rob1(nums[1:]),rob1(nums[:-1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "\n",
    "        n = len(nums)\n",
    "        dp = [[0,0] for _ in range(n)]\n",
    "        dp[0][0] = 0\n",
    "        dp[0][1] = nums[0]\n",
    "        if n >= 2:\n",
    "            dp[1][0] = nums[1]\n",
    "            dp[1][1] = nums[0]\n",
    "\n",
    "            for i in range(2,n):\n",
    "                ni = i\n",
    "                if i == n-1:  \n",
    "                    dp[i][0] = max(dp[i-1][0], dp[i-2][0]+nums[ni])\n",
    "                    dp[i][1] = max(dp[i-1][1], dp[i-2][1])\n",
    "                else:\n",
    "                    dp[i][0] = max(dp[i-1][0], dp[i-2][0]+nums[ni])\n",
    "                    dp[i][1] = max(dp[i-1][1], dp[i-2][1]+nums[ni]) \n",
    "        return max(dp[n-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob1(self, nums: List[int]) -> int:\n",
    "        f0 = f1 = 0\n",
    "        for x in nums:\n",
    "            f0, f1 = f1, max(f1, f0 + x)\n",
    "        return f1\n",
    "\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        return max((nums[0] + self.rob1(nums[2:-1]), self.rob1(nums[1:])))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        return max(nums[0]+self.rob1(nums[2:-1]),self.rob1(nums[1:]))\n",
    "    def rob1(self, nums: List[int]) ->int:\n",
    "        f0=f1=0\n",
    "        for i,x in enumerate(nums):\n",
    "            f0,f1 = f1,max(f1,f0+x)\n",
    "        return f1\n",
    "    \n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        def mouse(nums):\n",
    "            n = len(nums)\n",
    "            if n == 0:\n",
    "                return 0\n",
    "            elif n == 1:\n",
    "                return nums[0]\n",
    "            dp1 = [0 for _ in range(0, n )]\n",
    "            dp2 = [0 for _ in range(0, n )]\n",
    "            # 对于第一种情况\n",
    "            dp1[1] = dp1[0] = nums[0]\n",
    "            for i in range(2, n - 1):\n",
    "                dp1[i] = max(dp1[i - 1], dp1[i - 2] + nums[i])\n",
    "            # 对于第二种情况\n",
    "            dp2[0] = 0\n",
    "            dp2[1] = nums[1]\n",
    "            for i in range(2, n):\n",
    "                dp2[i] = max(dp2[i - 1], dp2[i - 2] + nums[i])\n",
    "\n",
    "            return max(dp2[n - 1], dp1[n - 2])\n",
    "        return mouse(nums)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        dp1 = [0] * n\n",
    "        dp2 = [0] * n\n",
    "        for i in range(n - 1):\n",
    "            dp1[i] = max(dp1[i - 1], dp1[i - 2] + nums[i])\n",
    "        for i in range(1, n):\n",
    "            dp2[i] = max(dp2[i - 1], dp2[i - 2] + nums[i])\n",
    "        return max(dp1[-2], dp2[-1]) if n > 1 else nums[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        def my_rob(nums):\n",
    "            cur = pre = 0\n",
    "            for num in nums:\n",
    "                cur, pre = max(cur, pre + num), cur\n",
    "            return cur\n",
    "        return max(my_rob(nums[1:]),my_rob(nums[:-1])) if len(nums)!=1 else nums[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob1(self, nums: List[int]) -> int:\n",
    "        f0 = f1 = 0\n",
    "        for i, x in enumerate(nums):\n",
    "            f0, f1 = f1, max(f1, f0 + x)\n",
    "        return f1\n",
    "\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        return max(nums[0] + self.rob1(nums[2:-1]), self.rob1(nums[1:]))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if len(nums)<=3:\n",
    "            return max(nums)\n",
    "        tmp = [[nums[0], max(nums[0], nums[1])], [nums[1], max(nums[1], nums[2])]]\n",
    "        for i in range(2, len(nums)-1):\n",
    "            tmp[0].append(max(tmp[0][i-1], tmp[0][i-2]+nums[i]))\n",
    "            tmp[1].append(max(tmp[1][i-1], tmp[1][i-2]+nums[i+1]))\n",
    "        # res1 = tmp[-1]\n",
    "        # tmp = [nums[1], max(nums[1], nums[2])]\n",
    "        # for i in range(2, len(nums)-1):\n",
    "        #     tmp.append(max(tmp[i-1], tmp[i-2]+nums[i+1]))\n",
    "        \n",
    "        return max(tmp[0][-1], tmp[1][-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        max_1 = None\n",
    "        max_2 = None\n",
    "        if n ==1:\n",
    "            return nums[0]\n",
    "        elif n==2:\n",
    "            return max(nums[0],nums[1])\n",
    "        nums1 = nums[:-1]\n",
    "        n = len(nums1)\n",
    "        if n ==1:\n",
    "            max_1 =  nums1[0]\n",
    "        elif n==2:\n",
    "            max_1 = max(nums1[0],nums1[1])\n",
    "        elif n == 3:\n",
    "            max_1 = max(nums1[0]+nums[2], nums1[1])\n",
    "        else:\n",
    "            dp1 = [0 for i in range(n)]\n",
    "            dp1[0] = nums1[0]\n",
    "            dp1[1] = max(nums1[0],nums1[1])\n",
    "            dp1[2] = max(nums1[0]+nums1[2], nums1[1])\n",
    "            for i in range(3,n):\n",
    "                dp1[i] = max(nums1[i]+dp1[i-2], nums1[i]+dp1[i-3])\n",
    "  \n",
    "        nums1 = nums[1:]\n",
    "        if n ==1:\n",
    "            max_2 = nums1[0]\n",
    "        elif n==2:\n",
    "            max_2 = max(nums1[0],nums1[1])\n",
    "        elif n == 3:\n",
    "            max_2 = max(nums1[0]+nums1[2], nums1[1])\n",
    "        else:\n",
    "            dp2 = [0 for i in range(n)]\n",
    "            dp2[0] = nums1[0]\n",
    "            dp2[1] = max(nums1[0],nums1[1])\n",
    "            dp2[2] = max(nums1[0]+nums1[2], nums1[1])\n",
    "            for i in range(3,n):\n",
    "                dp2[i] = max(nums1[i]+dp2[i-2], nums1[i]+dp2[i-3])\n",
    "        if max_1 != None or max_2!=None:\n",
    "            return max(max_1,max_2)\n",
    "        return max(max(dp1),max(dp2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        dp1, dp2 = [0] * n, [0] * n\n",
    "        for i in range(n - 1):\n",
    "            dp1[i] = max(dp1[i - 1], dp1[i - 2] + nums[i])\n",
    "        for i in range(1, n):\n",
    "            dp2[i] = max(dp2[i - 1], dp2[i - 2] + nums[i])\n",
    "        return max(dp1[-2], dp2[-1]) if n > 1 else nums[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "\n",
    "        # # 标识出最后一个房子偷还是不偷\n",
    "\n",
    "        n = len(nums)\n",
    "\n",
    "        # # x是房子序号，y是标识符\n",
    "        # # 时间复杂度是O(n)\n",
    "        # @ cache\n",
    "        # def dfs(x:int, y:int) -> int:\n",
    "\n",
    "        #     if y == 1:\n",
    "        #         if x < 1:\n",
    "        #             return 0\n",
    "        #     if y == 0:\n",
    "        #         if x < 0:\n",
    "        #             return 0\n",
    "            \n",
    "        #     return max(dfs(x-1, y), dfs(x-2, y) + nums[x])\n",
    "\n",
    "        # return max(dfs(n-2, 0), dfs(n-3, 1) + nums[n-1])\n",
    "\n",
    "\n",
    "        # 递归转递推 \n",
    "        # 创建一个二维数组，里面有两个值，分别表示偷与不偷最后一家的情况\n",
    "        dp = [[0, 0] for _ in range(n + 2)]\n",
    "\n",
    "        for i, v in enumerate(nums):\n",
    "            if i == 0:\n",
    "                dp[i+2][0] = max(dp[i][0] + v, dp[i+1][0])\n",
    "            else:\n",
    "                dp[i+2][0] = max(dp[i][0] + v, dp[i+1][0])\n",
    "                dp[i+2][1] = max(dp[i][1] + v, dp[i+1][1])\n",
    "            \n",
    "        return max(dp[-2][0], dp[-3][1] + nums[n-1])\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if len(nums)==1:return nums[0]\n",
    "        dp1, dp2 = [0] * (len(nums)-1), [0] * (len(nums)-1)\n",
    "        dp1[0], dp2[0] = nums[0], nums[1]\n",
    "        for i in range(1, len(nums)-1):\n",
    "            dp1[i] = max(dp1[i-1], dp1[i-2]+nums[i] if i-2>=0 else nums[i])\n",
    "            dp2[i] = max(dp2[i-1], dp2[i-2]+nums[i+1] if i-2>=0 else nums[i+1])\n",
    "        return max(dp1[-1], dp2[-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def simpleRob(self, nums:list[int], start, end) ->int:\n",
    "        n = end - start + 1\n",
    "        if n == 0:\n",
    "            return 0\n",
    "        if n == 1:\n",
    "            return nums[start]\n",
    "        dp = [0] * n\n",
    "        dp[0] = nums[start]\n",
    "        dp[1] = max(nums[start], nums[start + 1])\n",
    "        for i in range(2, n):\n",
    "            dp[i] = max(dp[i - 1], nums[i + start] + dp[i - 2])\n",
    "        return dp[n - 1]\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        if n == 0:\n",
    "            return 0\n",
    "        if n == 1:\n",
    "            return nums[0]\n",
    "        num1 = self.simpleRob(nums, 0, n - 2)\n",
    "        num2 = self.simpleRob(nums, 1, n - 1)\n",
    "        num3 = self.simpleRob(nums, 1, n - 2)\n",
    "        return max(max(num1, num2), num3)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        if n == 1:\n",
    "            return nums[0]\n",
    "        nums1 = nums[:-1]\n",
    "        dp = [0]*(n+1)\n",
    "        dp[0] = 0\n",
    "        dp[1] = nums1[0]\n",
    "        print(nums1[0])\n",
    "        for i in range(2,n):\n",
    "            dp[i] = max(nums1[i-1]+dp[i-2],dp[i-1])\n",
    "        \n",
    "        nums2 = nums[1:]\n",
    "        dp2 = [0]*(n+1)\n",
    "        dp2[0] = 0\n",
    "        dp2[1] = nums2[0]\n",
    "        for i in range(2,n):\n",
    "            dp2[i] = max(nums2[i-1]+dp2[i-2],dp2[i-1])\n",
    "        # print(dp,dp2)\n",
    "        return max(dp[n-1],dp2[n-1])\n",
    "        \n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        #头尾不能同时取得，也就是说答案是取0或者取n-1两个节点的最大值\n",
    "        def orirob(nums):\n",
    "            dp = [[0]*2 for _ in range(len(nums))]\n",
    "            dp[0][1] = nums[0]\n",
    "            for i in range(1,len(nums)):\n",
    "                dp[i][0] = max(dp[i-1][0],dp[i-1][1])\n",
    "                dp[i][1] = dp[i-1][0]+nums[i]\n",
    "            return max(dp[-1])\n",
    "        n = len(nums)\n",
    "        ans = max(nums) if n<=2 else max(orirob(nums[:-1]),orirob(nums[1:]))\n",
    "        return ans"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "# https://mp.weixin.qq.com/s/z44hk0MW14_mAQd7988mfw\n",
    "# 环形数组，只有2种情况：抢首不抢尾，抢尾不抢首\n",
    "\n",
    "# 节约空间法。要倒序，这里是自底向上\n",
    "# 解释：抢劫，自顶向下，就是从第一家往最后一家走。那么自底向上，那肯定倒着来啊，就是从最后一家往第一家走\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        # base case或者说边界条件\n",
    "        if n == 0:\n",
    "            return 0\n",
    "        \n",
    "        if n == 1:\n",
    "            return nums[0]\n",
    "\n",
    "        return max(self.robby(nums, 0, n-2), self.robby(nums, 1, n-1))\n",
    "\n",
    "    def robby(self, nums, start, end):\n",
    "        dp_i_1, dp_i_2 = 0, 0\n",
    "\n",
    "        dp_i = 0\n",
    "        # 环形数组。这里是倒序。因为这个方法\n",
    "        for i in range(end, start-1, -1):\n",
    "            dp_i = max(dp_i_1, nums[i] + dp_i_2)\n",
    "            # 这种写法是同时交换。不会把新的dp_i_1赋值给dp_i_2的。只会把就的dp_i_1赋值给dp_i_2\n",
    "            dp_i_1, dp_i_2 = dp_i, dp_i_1\n",
    "\n",
    "        return dp_i"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        #从来\n",
    "        n = len(nums)\n",
    "        f = [[0]*2 for i in range(110)]         \n",
    "        if  n==1:\n",
    "            return nums[0]\n",
    "        if n==2:\n",
    "            return max(nums[0],nums[1])\n",
    "        f[0][0] = 0\n",
    "        f[0][1] = nums[0]\n",
    "        for i in range(1,n):\n",
    "            for j in range(2):\n",
    "                if i==1:\n",
    "                    if j==0:\n",
    "                        f[i][j] = nums[1]\n",
    "                    else:\n",
    "                        f[i][j] = nums[0]\n",
    "                elif i==n-1 and j==1:\n",
    "                    f[i][j] = f[i-1][j]\n",
    "                else:\n",
    "                    f[i][j] = max(f[i-1][j],f[i-2][j]+nums[i])\n",
    "        return max(f[n-1][0],f[n-1][1])\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob1(self, nums):\n",
    "        f1 = f0 = 0\n",
    "        for i, x in enumerate(nums):\n",
    "            nf = max(f0+x, f1)\n",
    "            f0 = f1\n",
    "            f1 = nf\n",
    "        return f1\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        return max(nums[0] + self.rob1(nums[2:-1]), self.rob1(nums[1:]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        cur, pre = nums[0], 0\n",
    "        for i in range(1, n - 1):\n",
    "            cur, pre = max(cur, nums[i] + pre), cur\n",
    "        max1 = cur\n",
    "        cur, pre = 0, 0\n",
    "        for i in range(1, n):\n",
    "            cur, pre = max(cur, nums[i] + pre), cur\n",
    "        max2 = cur\n",
    "        return max(max1, max2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if len(nums)==1:\n",
    "            return nums[0]\n",
    "        def bianli(nums):\n",
    "            if len(nums)==1:\n",
    "                return nums[0]\n",
    "            n = len(nums)\n",
    "            dp = [0]*(n+1)\n",
    "            dp[1] = nums[0]\n",
    "            for i in range(2,n+1):\n",
    "                dp[i] = max(dp[i-1],dp[i-2]+nums[i-1])\n",
    "            return dp[-1]\n",
    "        return max(bianli(nums[1:]),bianli(nums[:-1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob_sub(self, nums: List[int]) -> int:\n",
    "        f0 = f1 = 0\n",
    "        for i, x in enumerate(nums):\n",
    "            f0, f1 = f1, max(f1, f0 + x)\n",
    "        return f1\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        return max(nums[0] + self.rob_sub(nums[2:-1]), self.rob_sub(nums[1:]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        def robrange(nums):\n",
    "            dp=[0]*len(nums)\n",
    "            dp[0]=nums[0]\n",
    "            dp[1]=max(dp[0],nums[1])\n",
    "            for i in range(2,len(nums)):\n",
    "                dp[i]=max(dp[i-1],dp[i-2]+nums[i])\n",
    "            return dp[-1]\n",
    "        if not nums:return 0\n",
    "        if len(nums)==1:return nums[0]\n",
    "        if len(nums)==2:return max(nums)\n",
    "        return max(robrange(nums[:-1]),robrange(nums[1:]))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "\n",
    "        def sub(n):\n",
    "            if len(n)<=2:\n",
    "                return max(n)\n",
    "            dp = [0 for _ in range(len(n)+1)]\n",
    "            dp[1] = n[0]\n",
    "            for i in range(2, len(n)+1):\n",
    "                dp[i] = max(dp[i-2]+n[i-1], dp[i-1])\n",
    "            return dp[-1]\n",
    "\n",
    "        if len(nums)<=2:\n",
    "            return max(nums)\n",
    "        n1 = nums[:-1]\n",
    "        n2 = nums[1:]\n",
    "        return max(sub(n1), sub(n2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "\n",
    "        n = len(nums)\n",
    "        dp = [[0,0] for _ in range(n)]\n",
    "        dp[0][0] = 0\n",
    "        dp[0][1] = nums[0]\n",
    "        if n >= 2:\n",
    "            dp[1][0] = nums[1]\n",
    "            dp[1][1] = nums[0]\n",
    "\n",
    "            for i in range(2,n):\n",
    "                if i == n-1:  \n",
    "                    dp[i][0] = max(dp[i-1][0], dp[i-2][0]+nums[i])\n",
    "                    dp[i][1] = max(dp[i-1][1], dp[i-2][1])\n",
    "                else:\n",
    "                    dp[i][0] = max(dp[i-1][0], dp[i-2][0]+nums[i])\n",
    "                    dp[i][1] = max(dp[i-1][1], dp[i-2][1]+nums[i]) \n",
    "        return max(dp[n-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "\n",
    "        # # 标识出最后一个房子偷还是不偷\n",
    "\n",
    "        n = len(nums)\n",
    "        # if n == 1:\n",
    "        #     return nums[0]\n",
    "\n",
    "        # # # x是房子序号，y是标识符\n",
    "        # # # 时间复杂度是O(n)\n",
    "        # @ cache\n",
    "        # def dfs(x:int, y:int) -> int:\n",
    "\n",
    "        #     if y == 1:\n",
    "        #         if x < 1:\n",
    "        #             return 0\n",
    "        #     if y == 0:\n",
    "        #         if x < 0:\n",
    "        #             return 0\n",
    "            \n",
    "        #     return max(dfs(x-1, y), dfs(x-2, y) + nums[x])\n",
    "\n",
    "        # return max(dfs(n-2, 0), dfs(n-1, 1))\n",
    "\n",
    "\n",
    "        # 递归转递推 \n",
    "        # 创建一个二维数组，里面有两个值，分别表示偷与不偷最后一家的情况\n",
    "        # dp = [[0, 0] for _ in range(n + 2)]\n",
    "\n",
    "        if n == 1:\n",
    "            return nums[0]\n",
    "\n",
    "        # for i, v in enumerate(nums):\n",
    "        #     if i == 0:\n",
    "        #         dp[i+2][0] = max(dp[i][0] + v, dp[i+1][0])\n",
    "        #     else:\n",
    "        #         dp[i+2][0] = max(dp[i][0] + v, dp[i+1][0])\n",
    "        #         dp[i+2][1] = max(dp[i][1] + v, dp[i+1][1])\n",
    "            \n",
    "        # return max(dp[-2][0], dp[-1][1])\n",
    "\n",
    "\n",
    "        # 状态压缩\n",
    "        f0 = f1 = 0\n",
    "        ff0 = ff1 = 0\n",
    "\n",
    "        if n == 1:\n",
    "            return nums[0]\n",
    "\n",
    "        for i, v in enumerate(nums):\n",
    "            if i == 0:\n",
    "                new_f = max(f0 + v, f1)\n",
    "            else:\n",
    "                new_f = max(f0 + v, f1)\n",
    "                new_ff = max(ff0 + v, ff1)\n",
    "                ff0, ff1 = ff1, new_ff\n",
    "            f0, f1 = f1, new_f\n",
    "            \n",
    "        \n",
    "        return max(f0, ff1)\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if len(nums)==1:\n",
    "            return nums[0]\n",
    "        def rob0(nums: List[int]) -> int:\n",
    "            dp = [0 for i in range(len(nums) + 1)]\n",
    "            dp[1] = nums[0]\n",
    "            for i in range(2, len(nums) + 1):\n",
    "                dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1])\n",
    "            return dp[-1]\n",
    "        a=rob0(nums[:-1])\n",
    "        b=rob0(nums[1:])\n",
    "        return max(a,b)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def simpleRob(self, nums:list[int], start, end) ->int:\n",
    "        n = end - start + 1\n",
    "        if n == 0:\n",
    "            return 0\n",
    "        if n == 1:\n",
    "            return nums[start]\n",
    "        dp = [0] * n\n",
    "        dp[0] = nums[start]\n",
    "        dp[1] = max(nums[start], nums[start + 1])\n",
    "        for i in range(2, n):\n",
    "            dp[i] = max(dp[i - 1], nums[i + start] + dp[i - 2])\n",
    "        return dp[n - 1]\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        if n == 0:\n",
    "            return 0\n",
    "        if n == 1:\n",
    "            return nums[0]\n",
    "        num1 = self.simpleRob(nums, 0, n - 2)\n",
    "        num2 = self.simpleRob(nums, 1, n - 1)\n",
    "        num3 = self.simpleRob(nums, 1, n - 2)\n",
    "        return max(max(num1, num2), num3)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if len(nums) == 1:\n",
    "            return nums[0]\n",
    "        \n",
    "        def rob_range(nums, start, end):\n",
    "            dp_i = 0\n",
    "            dp_i_1 = 0\n",
    "            dp_i_2 = 0\n",
    "            for i in range(end, start-1, -1):\n",
    "                dp_i = max(dp_i_1, nums[i] + dp_i_2)\n",
    "                dp_i_2 = dp_i_1\n",
    "                dp_i_1 = dp_i\n",
    "            return dp_i\n",
    "\n",
    "        return max(rob_range(nums, 0, len(nums)-2), rob_range(nums, 1, len(nums)-1))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        try:\n",
    "            dp1=[0]*len(nums)\n",
    "            dp1[0],dp1[1]=nums[0],max(nums[:2])\n",
    "            for i in range(2,len(nums)):\n",
    "                dp1[i]=max(dp1[i-1],dp1[i-2]+nums[i])\n",
    "            \n",
    "            dp2=[0]*len(nums)\n",
    "            dp2[1]=nums[1]\n",
    "            for i in range(2,len(nums)):\n",
    "                dp2[i]=max(dp2[i-1],dp2[i-2]+nums[i])\n",
    "\n",
    "            return max(dp1[-2],dp2[-1])\n",
    "        except:\n",
    "            try:\n",
    "                return max(nums[:2])\n",
    "            except:\n",
    "                return nums[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        ##可以循环相比较之前的就是多了最后一个限制->最后一个不能和第一个相连\n",
    "        ##可以分类讨论 1.没有第一个最后肯定不会相连 2.没有最后一个最后也不会相连 最后max\n",
    "        if len(nums)==1:return nums[0]\n",
    "        def dp_nums(nums):##正常的房屋偷盗\n",
    "            dp=[0 for i in range(len(nums)+1)]\n",
    "            dp[1]=nums[0]\n",
    "            for i in range(2,len(dp)):\n",
    "                dp[i]=max(dp[i-1],dp[i-2]+nums[i-1])\n",
    "            return dp[-1]\n",
    "        return max(dp_nums(nums[1:]),dp_nums(nums[:-1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        max_1 = None\n",
    "        max_2 = None\n",
    "        if n ==1:\n",
    "            return nums[0]\n",
    "        elif n==2:\n",
    "            return max(nums[0],nums[1])\n",
    "        nums1 = nums[:-1]\n",
    "        n = len(nums1)\n",
    "        if n ==1:\n",
    "            max_1 =  nums1[0]\n",
    "        elif n==2:\n",
    "            max_1 = max(nums1[0],nums1[1])\n",
    "        elif n == 3:\n",
    "            max_1 = max(nums1[0]+nums[2], nums1[1])\n",
    "        else:\n",
    "            dp1 = [0 for i in range(n)]\n",
    "            dp1[0] = nums1[0]\n",
    "            dp1[1] = max(nums1[0],nums1[1])\n",
    "            dp1[2] = max(nums1[0]+nums1[2], nums1[1])\n",
    "            for i in range(3,n):\n",
    "                dp1[i] = max(nums1[i]+dp1[i-2], nums1[i]+dp1[i-3])\n",
    "  \n",
    "        nums1 = nums[1:]\n",
    "        if n ==1:\n",
    "            max_2 = nums1[0]\n",
    "        elif n==2:\n",
    "            max_2 = max(nums1[0],nums1[1])\n",
    "        elif n == 3:\n",
    "            max_2 = max(nums1[0]+nums1[2], nums1[1])\n",
    "        else:\n",
    "            dp2 = [0 for i in range(n)]\n",
    "            dp2[0] = nums1[0]\n",
    "            dp2[1] = max(nums1[0],nums1[1])\n",
    "            dp2[2] = max(nums1[0]+nums1[2], nums1[1])\n",
    "            for i in range(3,n):\n",
    "                dp2[i] = max(nums1[i]+dp2[i-2], nums1[i]+dp2[i-3])\n",
    "        if max_1 != None or max_2!=None:\n",
    "            return max(max_1,max_2)\n",
    "        return max(max(dp1),max(dp2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "\n",
    "        n = len(nums)\n",
    "        dp = [[0,0] for _ in range(n)]\n",
    "        dp[0][0] = 0\n",
    "        dp[0][1] = nums[0]\n",
    "        if n >= 2:\n",
    "            dp[1][0] = nums[1]\n",
    "            dp[1][1] = nums[0]\n",
    "\n",
    "            for i in range(2,n):\n",
    "                ni = i\n",
    "                if i == n-1:  \n",
    "                    dp[i][0] = max(dp[i-1][0], dp[i-2][0]+nums[ni])\n",
    "                    dp[i][1] = max(dp[i-1][1], dp[i-2][1])\n",
    "                else:\n",
    "                    dp[i][0] = max(dp[i-1][0], dp[i-2][0]+nums[ni])\n",
    "                    dp[i][1] = max(dp[i-1][1], dp[i-2][1]+nums[ni]) \n",
    "        return max(dp[n-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        def robrange(start,end):\n",
    "            tmp=nums[start:end]\n",
    "            dp=[0]*len(tmp)\n",
    "            dp[0]=tmp[0]\n",
    "            dp[1]=max(dp[0],tmp[1])\n",
    "            for i in range(2,len(tmp)):\n",
    "                dp[i]=max(dp[i-1],dp[i-2]+tmp[i])\n",
    "            return dp[-1]\n",
    "        if not nums:return 0\n",
    "        if len(nums)==1:return nums[0]\n",
    "        if len(nums)==2:return max(nums[0],nums[1])\n",
    "        return max(robrange(0,len(nums)-1),robrange(1,len(nums)))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "\n",
    "        def get_max_rob(cur_nums):\n",
    "            cur1, pre1 = 0, 0\n",
    "            for x in cur_nums:\n",
    "                cur1, pre1 = max(cur1, pre1+x), cur1\n",
    "            return cur1\n",
    "        \n",
    "        return max(get_max_rob(nums[1:]),get_max_rob(nums[:-1])) if len(nums)!=1 else nums[0]\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        ans1 = 0\n",
    "        ans2 = 0\n",
    "        n = len(nums)\n",
    "        if n == 1:\n",
    "            return nums[0]\n",
    "        if n == 2:\n",
    "            return max(nums[0], nums[1])\n",
    "        dp = [0] * n\n",
    "        dp[0] = nums[0]\n",
    "        dp[1] = max(nums[0], nums[1])\n",
    "        for i in range(2, n-1):\n",
    "            dp[i] = max(dp[i-2] + nums[i], dp[i-1])\n",
    "        ans1 = dp[n-2]\n",
    "        dp = [0] * n\n",
    "        dp[1] = nums[1]\n",
    "        dp[2] = max(nums[1], nums[2])\n",
    "        for i in range(3, n):\n",
    "            dp[i] = max(dp[i-2] + nums[i], dp[i-1])\n",
    "        ans2 = dp[n-1]\n",
    "        return max(ans1, ans2)\n",
    "\n",
    "            \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        def rob1(nums):\n",
    "            n=len(nums)\n",
    "            if n==0:\n",
    "                return 0\n",
    "            if n==1:\n",
    "                return nums[0]\n",
    "            if n==2:\n",
    "                return max(nums)\n",
    "            dp=[0]*n\n",
    "            dp[0],dp[1]=nums[0],nums[1]\n",
    "            for i in range(2,n):\n",
    "                dp[i]=max(dp[i-1],max(dp[:i-1])+nums[i])\n",
    "            return max(dp)\n",
    "        return max(nums[0]+rob1(nums[2:-1]),rob1(nums[1:]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if not nums: return 0\n",
    "        if len(nums) <= 3: return max(nums)\n",
    "        arr1, arr2 = nums[1:], nums[:-1]\n",
    "        def rob(arr):\n",
    "            n = len(arr)\n",
    "            dp = [0] * n\n",
    "            dp[0], dp[1] = arr[0], max(arr[0],arr[1])\n",
    "            for i in range(1,n):\n",
    "                dp[i] = max(dp[i-1], dp[i-2]+arr[i])\n",
    "            return dp[n-1]\n",
    "        return max(rob(arr1),rob(arr2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "\n",
    "        '''\n",
    "        if len(nums) == 1: return nums[0]\n",
    "        elif len(nums) == 2: return max(nums[0],nums[1])\n",
    "\n",
    "        # 要么偷第一间不偷最后一间 要么反过来\n",
    "\n",
    "        dp = [0] * len(nums)\n",
    "        dp[0] = nums[0]\n",
    "        dp[1] = max(nums[0],nums[1])\n",
    "\n",
    "        for i in range(2,len(nums)-1):\n",
    "            dp[i] = max(dp[i-1],dp[i-2]+nums[i])\n",
    "\n",
    "        dp1 = [0] * len(nums)\n",
    "        dp1[0] = nums[1]\n",
    "        dp1[1] = max(nums[1],nums[2])\n",
    "\n",
    "        for i in range(3,len(nums)):\n",
    "            dp[i] = max(dp[i-1],dp[i-2]+nums[i])\n",
    "\n",
    "        return max(max(dp1),max(dp))\n",
    "        '''\n",
    "        def my_rob(nums):\n",
    "            cur, pre = 0, 0\n",
    "            for num in nums:\n",
    "                cur, pre = max(pre + num, cur), cur\n",
    "            return cur\n",
    "        return max(my_rob(nums[:-1]),my_rob(nums[1:])) if len(nums) != 1 else nums[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "\n",
    "    def rob1(self, nums: List[int]) -> int:\n",
    "        f0 = f1 = 0\n",
    "        for i, x in enumerate(nums):\n",
    "            f0, f1 = f1, max(f1, f0 + x)\n",
    "        return f1\n",
    "\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        return max(nums[0] + self.rob1(nums[2:-1]), self.rob1(nums[1:]))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "\n",
    "        def dp(nums: List[int]) -> int:\n",
    "            N = len(nums)\n",
    "            # 定义状态：dp[i]表示从第i个房屋开始偷窃，能够偷到的最高金额\n",
    "            dp = [0 for i in range(N)]\n",
    "            for i in range(N-1, -1, -1):\n",
    "                if i == N-1:\n",
    "                    dp[i] = nums[i]\n",
    "                elif i == N-2:\n",
    "                    dp[i] = max(nums[i], nums[i+1])\n",
    "                else:\n",
    "                    dp[i] = max(dp[i+1], nums[i] + dp[i+2])\n",
    "            return dp[0]\n",
    "        if len(nums) == 1:\n",
    "            return nums[0]\n",
    "        res = max(dp(nums[:-1]), dp(nums[1:]))\n",
    "        return res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        # 可能可以截断一下，然后分类讨论\n",
    "        # 复杂度翻倍，因为选了一个另外一个就不选\n",
    "\n",
    "        if len(nums) == 0:\n",
    "            return 0\n",
    "        \n",
    "        if len(nums) == 1:\n",
    "            return nums[0]\n",
    "        \n",
    "        if len(nums) == 2 or len(nums) == 3:\n",
    "            return max(nums)\n",
    "        \n",
    "        res = [0] * (len(nums)-1)\n",
    "        res[0] = nums[0]\n",
    "        res[1] = nums[1]\n",
    "\n",
    "        for i in range(2, len(nums)-1):\n",
    "            if i-3 >= 0:\n",
    "                res[i] = max(res[i-2], res[i-3]) + nums[i]\n",
    "            else:\n",
    "                res[i] = res[i-2] + nums[i]\n",
    "        \n",
    "        tmp = max(res[-2:])\n",
    "\n",
    "        res = [0] * (len(nums)-1)\n",
    "        res[0] = nums[1]\n",
    "        res[1] = nums[2]\n",
    "        for i in range(3, len(nums)):\n",
    "            if i-4 >= 0:\n",
    "                res[i-1] = max(res[i-3], res[i-4]) + nums[i]\n",
    "            else:\n",
    "                res[i-1] = res[i-3] + nums[i]\n",
    "        \n",
    "        return max(max(res[-2:]), tmp)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        max_1 = None\n",
    "        max_2 = None\n",
    "        if n ==1:\n",
    "            return nums[0]\n",
    "        elif n==2:\n",
    "            return max(nums[0],nums[1])\n",
    "        nums1 = nums[:-1]\n",
    "        n = len(nums1)\n",
    "        if n ==1:\n",
    "            max_1 =  nums1[0]\n",
    "        elif n==2:\n",
    "            max_1 = max(nums1[0],nums1[1])\n",
    "        elif n == 3:\n",
    "            max_1 = max(nums1[0]+nums[2], nums1[1])\n",
    "        else:\n",
    "            dp1 = [0 for i in range(n)]\n",
    "            dp1[0] = nums1[0]\n",
    "            dp1[1] = max(nums1[0],nums1[1])\n",
    "            dp1[2] = max(nums1[0]+nums1[2], nums1[1])\n",
    "            for i in range(3,n):\n",
    "                dp1[i] = max(nums1[i]+dp1[i-2], nums1[i]+dp1[i-3])\n",
    "  \n",
    "        nums1 = nums[1:]\n",
    "        if n ==1:\n",
    "            max_2 = nums1[0]\n",
    "        elif n==2:\n",
    "            max_2 = max(nums1[0],nums1[1])\n",
    "        elif n == 3:\n",
    "            max_2 = max(nums1[0]+nums1[2], nums1[1])\n",
    "        else:\n",
    "            dp2 = [0 for i in range(n)]\n",
    "            dp2[0] = nums1[0]\n",
    "            dp2[1] = max(nums1[0],nums1[1])\n",
    "            dp2[2] = max(nums1[0]+nums1[2], nums1[1])\n",
    "            for i in range(3,n):\n",
    "                dp2[i] = max(nums1[i]+dp2[i-2], nums1[i]+dp2[i-3])\n",
    "        if max_1 != None or max_2!=None:\n",
    "            return max(max_1,max_2)\n",
    "        return max(max(dp1),max(dp2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "            # tmd f = g = [0] * 101, f g 指向同一个数组\n",
    "            f = [0] * 101\n",
    "            g = [0] * 101\n",
    "            n = len(nums)\n",
    "\n",
    "            if n == 1:\n",
    "                return nums[0]\n",
    "            if n == 2:\n",
    "                return max(nums[0], nums[1])\n",
    "            g[0] = 0\n",
    "            f[0] = nums[0]\n",
    "            f[1] = nums[0]\n",
    "            g[1] = nums[1]\n",
    "\n",
    "            for i in range(2, n):\n",
    "                if i == n-1:\n",
    "                        f[i] = f[i-1]\n",
    "                        g[i] = max(g[i-1], g[i-2] + nums[i])\n",
    "                else:\n",
    "                    f[i] = max(f[i-1], f[i-2] + nums[i])\n",
    "                    g[i] = max(g[i-1], g[i-2] + nums[i])\n",
    "\n",
    "            return max(f[n-1], g[n-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        def fuc(num):\n",
    "            p,q = 0,0\n",
    "            for i in num:\n",
    "                p,q = q,max(q,p+i)\n",
    "            return max(p,q)\n",
    "        \n",
    "        if len(nums) <= 2:\n",
    "            return max(nums)\n",
    "        # 第一个和最后一个不能同时抢，\n",
    "        return max(fuc(nums[1:]),fuc(nums[:-1])) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "\n",
    "        # # 标识出最后一个房子偷还是不偷\n",
    "\n",
    "        n = len(nums)\n",
    "        # if n == 1:\n",
    "        #     return nums[0]\n",
    "\n",
    "        # # # x是房子序号，y是标识符\n",
    "        # # # 时间复杂度是O(n)\n",
    "        # @ cache\n",
    "        # def dfs(x:int, y:int) -> int:\n",
    "\n",
    "        #     if y == 1:\n",
    "        #         if x < 1:\n",
    "        #             return 0\n",
    "        #     if y == 0:\n",
    "        #         if x < 0:\n",
    "        #             return 0\n",
    "            \n",
    "        #     return max(dfs(x-1, y), dfs(x-2, y) + nums[x])\n",
    "\n",
    "        # return max(dfs(n-2, 0), dfs(n-1, 1))\n",
    "\n",
    "\n",
    "        # 递归转递推 \n",
    "        # 创建一个二维数组，里面有两个值，分别表示偷与不偷最后一家的情况\n",
    "        dp = [[0, 0] for _ in range(n + 2)]\n",
    "\n",
    "        if n == 1:\n",
    "            return nums[0]\n",
    "\n",
    "        for i, v in enumerate(nums):\n",
    "            if i == 0:\n",
    "                dp[i+2][0] = max(dp[i][0] + v, dp[i+1][0])\n",
    "            else:\n",
    "                dp[i+2][0] = max(dp[i][0] + v, dp[i+1][0])\n",
    "                dp[i+2][1] = max(dp[i][1] + v, dp[i+1][1])\n",
    "            \n",
    "        return max(dp[-2][0], dp[-1][1])\n",
    "\n",
    "\n",
    "        # 状态压缩\n",
    "        # f0 = f1 = 0\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        \"\"\"\n",
    "        dp[i][0] 表示前i个元素中，不取第i个元素的最大值\n",
    "        dp[i][1] 表示前i个元素中，取第i个元素的最大值\n",
    "\n",
    "        res = max(dp[n-1][0], dp[n-1][1])\n",
    "        \"\"\"\n",
    "        n = len(nums)\n",
    "        if n == 1:\n",
    "            return nums[0]\n",
    "        if n == 2:\n",
    "            return max(nums)\n",
    "        \n",
    "        def func(arr):\n",
    "            n = len(arr)\n",
    "            dp = [[0]*2 for _ in range(n)]\n",
    "            dp[0][1] = arr[0]\n",
    "\n",
    "            for i in range(1, len(arr)):\n",
    "                dp[i][0] = max(dp[i-1][0], dp[i-1][1])\n",
    "                dp[i][1] = dp[i-1][0] + arr[i]\n",
    "\n",
    "            return max(dp[-1][0], dp[-1][1])\n",
    "        \n",
    "        print(nums[:-1], nums[1:])\n",
    "        return max(func(nums[:-1]), func(nums[1:]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        def rob1(arr):\n",
    "            n = len(arr)\n",
    "            if n == 0:\n",
    "                return 0\n",
    "            if n == 1:\n",
    "                return arr[0]\n",
    "            dp = [0] * (n + 1)\n",
    "            dp[0] = arr[0]\n",
    "            dp[1] = max(arr[0], arr[1])\n",
    "            for i in range(2, n):\n",
    "                dp[i] = max(dp[i - 1], dp[i - 2] + arr[i])\n",
    "            return dp[n - 1]\n",
    "        l = len(nums)\n",
    "        if l == 1:\n",
    "            return nums[0]\n",
    "        if l == 0:\n",
    "            return 0\n",
    "        value1 = rob1(nums[:-1])\n",
    "        value2 = rob1(nums[1:])\n",
    "        print(value1, value2)\n",
    "        return max(value1, value2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n=len(nums)\n",
    "        k=n*[0]\n",
    "        k[0]=nums[0]\n",
    "        if n==1:\n",
    "            return k[0]\n",
    "        if n>=2:\n",
    "            if nums[1]>k[0]:\n",
    "                k[1]=nums[1]\n",
    "            else:\n",
    "                k[1]=k[0]\n",
    "        if n==2:\n",
    "            return k[1]\n",
    "        k1=k.copy()\n",
    "        k2=k.copy()\n",
    "        k2[0]=nums[1]\n",
    "        k2[1]=max(nums[1],nums[2])\n",
    "\n",
    "        \n",
    "        for i in range (2,n-1):\n",
    "            k1[i]=max(k1[i-1],k1[i-2]+nums[i])\n",
    "            k2[i]=max(k2[i-1],k2[i-2]+nums[i+1])\n",
    "        \n",
    "        k[n-1]=max(k[n-1],k1[n-2],k2[n-2])\n",
    "        return k[n-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        #由于第一间房屋和最后一间房屋不能同时偷盗，所以把数组拆分为两个子数组\n",
    "        if len(nums) > 1:\n",
    "            nums1 = nums[:len(nums) - 1]\n",
    "            nums2 = nums[1:]\n",
    "\n",
    "            dp1 = [nums1[0]] #建立dp数组\n",
    "            if len(nums1) > 1: \n",
    "                dp1.append(nums1[1]) if nums1[1] > nums1[0] else dp1.append(nums1[0]) #初始化dp数组前两项\n",
    "                for x in range(2, len(nums1)):\n",
    "                    if(dp1[x - 2] + nums1[x] > dp1[x - 1]): #打劫最后一间房屋的方案\n",
    "                        dp1.append(dp1[x - 2] + nums1[x])\n",
    "                    else: #不打劫最后一间房屋的方案\n",
    "                        dp1.append(dp1[x - 1])\n",
    "            \n",
    "            dp2 = [nums2[0]] #建立dp数组\n",
    "            if len(nums2) > 1: \n",
    "                dp2.append(nums2[1]) if nums2[1] > nums2[0] else dp2.append(nums2[0]) #初始化dp数组前两项\n",
    "                for x in range(2, len(nums2)):\n",
    "                    if(dp2[x - 2] + nums2[x] > dp2[x - 1]): #打劫最后一间房屋的方案\n",
    "                        dp2.append(dp2[x - 2] + nums2[x])\n",
    "                    else: #不打劫最后一间房屋的方案\n",
    "                        dp2.append(dp2[x - 1])\n",
    "            \n",
    "            return max(dp1.pop(), dp2.pop())\n",
    "        else:\n",
    "            return nums[0]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if len(nums) <= 3:\n",
    "            return max(nums)\n",
    "        dp=[0] * (len(nums)+2)\n",
    "        start=res=0\n",
    "        l=len(nums)-1\n",
    "\n",
    "        while start < 2:\n",
    "            for i in range(start,start+l):\n",
    "                dp[i+2]=max(dp[i+1],dp[i]+nums[i])\n",
    "            start+=1\n",
    "            res=max(max(dp),res)\n",
    "            print(dp,res)\n",
    "            dp=[0] * (len(nums)+2)\n",
    "        return res\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        n = len(nums)\n",
    "        if n == 1:\n",
    "            return nums[0]\n",
    "        elif n == 2:\n",
    "            return max(nums[0],nums[1])\n",
    "        else:\n",
    "            # 偷第一家\n",
    "            dp1 = [0]*n\n",
    "            dp1[0] = nums[0]\n",
    "            dp1[1] = nums[0]\n",
    "            for i in range(2,n-1):\n",
    "                dp1[i] = max(dp1[i-1],dp1[i-2]+nums[i])\n",
    "            \n",
    "            # 偷第二家\n",
    "            dp2 = [0]*n\n",
    "            dp2[0] = 0\n",
    "            dp2[1] = nums[1]\n",
    "            for i in range(2,n):\n",
    "                dp2[i] = max(dp2[i-1],dp2[i-2]+nums[i])\n",
    "            return max(dp1[n-2],dp2[n-1])\n",
    "            \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "        if len(nums) == 1:\n",
    "            return nums[0]\n",
    "        # 从第二个房间开始偷到最后 vs 从第一个房间开始偷到倒数第一个房间\n",
    "        dp1 = [[0] * 2 for i in range(len(nums))]\n",
    "        dp2 = [[0] * 2 for i in range(len(nums))]\n",
    "\n",
    "        dp1[1][1] = nums[1]\n",
    "        for i in range(2, len(nums)):\n",
    "            dp1[i][0] = max(dp1[i-1][0], dp1[i-1][1])\n",
    "            dp1[i][1] = dp1[i-1][0] + nums[i]\n",
    "\n",
    "        dp2[0][1] = nums[0]\n",
    "        for i in range(1, len(nums) - 1):\n",
    "            dp2[i][0] = max(dp2[i-1][0], dp2[i-1][1])\n",
    "            dp2[i][1] = dp2[i-1][0] + nums[i]\n",
    "\n",
    "        return max(max(dp1[-1]), max(dp2[-2]))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import collections\n",
    "\n",
    "class Solution:\n",
    "    def rob(self, nums: List[int]) -> int:\n",
    "\n",
    "        n=len(nums)\n",
    "        if n==0:\n",
    "            return 0\n",
    "        elif n==1:\n",
    "            return nums[0]\n",
    "        elif n==2:\n",
    "            return max(nums)\n",
    "        n=n-1\n",
    "        a=nums.pop(-1)\n",
    "        nums1=nums\n",
    "        \n",
    "        dp1=[0]*n\n",
    "        dp1[0]=nums1[0]\n",
    "        dp1[1]=max(nums1[0],nums1[1])\n",
    "        for i in range(2,n):\n",
    "            dp1[i]=max(dp1[i-2]+nums1[i],dp1[i-1])\n",
    "        # nums2=nums\n",
    "        nums.append(a)\n",
    "        nums.pop(0)\n",
    "        nums2=nums\n",
    "        dp2=[0]*n\n",
    "        dp2[0]=nums2[0]\n",
    "        \n",
    "        dp2[1]=max(nums2[0],nums2[1])\n",
    "        for i in range(2,n):\n",
    "            dp2[i]=max(dp2[i-2]+nums2[i],dp2[i-1])\n",
    "        \n",
    "        return max(dp1[n-1],dp2[n-1])"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 2
}
